James
James

Reputation: 818

Safari sending empty file to server on File upload via FormData

We're trying to upload an blob of an image that's been cropped.

The following code works in Chrome and Firefox, however we're having issues with Safari.

The File appears to well formed before it is appended to the FormData unfortunately Safari does not give enough support to view the file once attached to the FormData

Our problem is that once it's sent to our server dumping out var_dump( $_FILES['file'] ) shows the file to empty (it's size property is 0).

I believe this to be down Safari's "basic" support of FormData, but Polyfillers I've tried to use have not resolved the issue. Is there a way around this?

// From Controller
$scope.uploadImage = function( croppedImage ) {
  $scope.uploadingImage = true;
  var imageBlob = dataURItoBlob( croppedImage );
  var options = {
    action: 'upload_file',
    format: 'media_item',
    output: 'json'
  }

  var file = new File( [ imageBlob ], $scope.filename,{
    type: 'image/png'
  });

  console.log( file.size );

  MediaService.uploadImage( file, options ).then(
    function( response ) {
      $modalInstance.close( response );
    }
  ).finally(
    function() {
      $scope.uploadingImage = false;
    }
  )
};

// From MediaService (angular service
uploadImage: function( image, params ){
  var formData = new FormData();
  formData.append( 'action', params.action );
  formData.append( 'format', params.format);
  formData.append( 'file', image );

  //The POST header needs to be multipart/form-data
  $http.defaults.headers.post = {};

  return $http( {
    method: 'POST',
    url: apiMediaUrl,
    data: formData,
    transformRequest: angular.identity,
    headers: { 'Content-Type': undefined },
    withCredentials: true
  } )
  .then(
    function( response ) {
      return response.data.payload;
    },
    function( errorResponse ) {
      throw errorResponse.data.payload || errorResponse.data;
    }
  );
}

Upvotes: 2

Views: 2966

Answers (1)

James
James

Reputation: 818

After a day of pulling my hair out, finally got this.

By sending the imageBlob instead of creating a new File to the MediaService and the appending a filename.

So

// From Controller
$scope.uploadImage = function( croppedImage ) {
  $scope.uploadingImage = true;
  var imageBlob = dataURItoBlob( croppedImage );
  var options = {
    action: 'upload_file',
    format: 'media_item',
    output: 'json',
    params: 'filename'
  }

  // no longer convert to file

  MediaService.uploadImage( imageBlob, options ).then(
    function( response ) {
      $modalInstance.close( response );
    }
  ).finally(
    function() {
      $scope.uploadingImage = false;
    }
  )
};

// From MediaService (angular service
uploadImage: function( image, params ){
  var formData = new FormData();
  formData.append( 'action', params.action );
  formData.append( 'format', params.format);

  /*
   *  Add file name here
   */
  formData.append( 'file', image, params.filename );

  //The POST header needs to be multipart/form-data
  $http.defaults.headers.post = {};

  return $http( {
    method: 'POST',
    url: apiMediaUrl,
    data: formData,
    transformRequest: angular.identity,
    headers: { 'Content-Type': undefined },
    withCredentials: true
  } )
  .then(
    function( response ) {
      return response.data.payload;
    },
    function( errorResponse ) {
      throw errorResponse.data.payload || errorResponse.data;
    }
  );
}

Upvotes: 1

Related Questions