4

I'm using FormData to upload files with AJAX (for progress bars, although I've omitted that code for simplicity):

<form method="post" enctype="multipart/form-data">
  <input type="file" name="file" />
</form>
$(document).ready(function() {
  $(document).on("change", ":file", function() {
    var i;
    for (i = 0; i < this.files.length; i++) {
      var file = this.files[i];
      uploadFile(this.name, file);
    }
  }
}

function uploadFile(inputName, file) {
  var formData = new FormData();
  formData.append(inputName, file);
  $.ajax({
    type: "POST",
    url: "/api/newfile",
    data: formData,
    timeout: 0,
    cache: false,
    contentType: false,
    processData: false
  }) // some code omitted...
}

This works fine. Next, I have the same code in an Apache Cordova phone application, but I add a "Take Picture" button as an option underneath each file input which uses navigator.camera.getPicture and FileEntry.file to get the File object and call the same uploadFile function:

$(document).ready(function() {
  $("input:file").each(function(index) {
    $("<button class='take_picture_button ui-btn'>Take Picture</button>").insertAfter($(this));
  });

  $(document).on("click", ".take_picture_button", function(e) {
    $inputFileElement = $(this).prev("input:file").first();
    navigator.camera.getPicture(function(fileURI) {
      window.resolveLocalFileSystemURL(fileURI, function(fileEntry) {
        fileEntry.file(function(file) {
          uploadFile($inputFileElement.attr("name"), file);
        });
      });
    });
  });
}); // some code omitted...

This does not work - the server just gets "[object Object]" instead of the file.

Connecting Chrome Developer Tools to the Android app, I see this part of the request payload in the failing case:

------WebKitFormBoundaryThmH13yROUHXJ4jB
Content-Disposition: form-data; name="identity_file[file]"

[object Object]

In the working case:

------WebKitFormBoundaryztHB86BlcKBZ9dPX
Content-Disposition: form-data; name="identity_file[file]"; filename="0DuheTR.jpg"
Content-Type: image/jpeg

I guess in the working case it doesn't actually show the file payload, but I can confirm it works.

It seems that in the failing case, it doesn't see the File object correctly, because it doesn't automatically add the filename attribute, nor the Content-Type, and also seems to just do a naive toString for the body. The app is built with Cordova 6.2.0 and running on Android 6.0.1.

I ran console.dir on the File object in both cases and they look about the same, although the proto in the failing case is Object instead of File - why?

Working:

File
  lastModified: 1458490019474
  lastModifiedDate: Sun Mar 20 2016 09:06:59 GMT-0700 (PDT)
  name: "0DuheTR.jpg"
  size: 152054
  type: "image/jpeg"
  webkitRelativePath: ""
  __proto__: File

Failing:

File
  end: 785009
  lastModified: 1469992497000
  lastModifiedDate: 1469992497000
  localURL: "cdvfile://localhost/cache-external/1469992495873.jpg"
  name: "1469992495873.jpg"
  size: 785009
  start: 0
  type: "image/jpeg"
  __proto__: Object
Kevin
  • 266
  • 3
  • 16
  • Perhaps the FileEntry.file is not working with the proper format? You could also try to add the result in a Blob and create a File object that way. new Blob([data], {type: "image/png"}); – Bram Aug 01 '16 at 11:04
  • @Bram Hi Bram, I'm not sure what you mean because File inherits from Blob. – Kevin Aug 02 '16 at 02:47

3 Answers3

1

The question has been answered here. You'll need to further convert the file object into Blob, using FileReader.

I just quote the gist of it here:

var formData = new FormData();
window.resolveLocalFileSystemURL(fileURI, function(fileEntry) {
    fileEntry.file(function(file) {
        var reader = new FileReader();
        reader.onloadend = function(e) {
            var imgBlob = new Blob([this.result], {type:"image/jpeg"});
            formData.append('image', imgBlob);
            //post formData here
        };
        reader.readAsArrayBuffer(file);
    });
});
Henry Luo
  • 354
  • 4
  • 10
0

I think it is the content-type not set to file multipart/form-data, pass a dummy form that the enctype is multipart/form-data and then append the field you want to add to it, i.e

<form method="post" enctype="multipart/form-data"></form>

var form=new FormData($("form"));

I think that should atleast help.

Theophilus Omoregbee
  • 2,463
  • 1
  • 22
  • 33
  • Correct point, but in my zeal to simplify my code snippet, I left that out from the snippet but it exists in the code. I'm quite sure now (writing a question helps clarify!) the issue is that FileEntry.file returns a "File" object that doesn't inherit from File. I think it's a bug in the embedded WebView. I'm currently investigating how to get a proper File object from it... – Kevin Jul 31 '16 at 21:25
  • owk cool, would still investigate too on it , if u'd finallly got pls don't fail to comment or share – Theophilus Omoregbee Aug 01 '16 at 09:01
0

Use cordova file transfer plugin.There you need to give the file URL to upload.You can find the reference here link

Homen
  • 1,222
  • 1
  • 12
  • 16
  • This worked. It was a bit annoying because I didn't have all of the convenience of jQuery.ajax (for example, having to call `jQuery.parseJSON(result.response)`). – Kevin Aug 02 '16 at 02:46