Search code examples
javascriptandroidfilecordovaform-data

FormData in Cordova 5.1.1 with Camera Operation not working


I am attempting to use the Cordova camera plugin to take a photo. After the photo is taken I create a new FormData object to append the image to. I take the Base64 Image created by the camera and convert it to a Blob before appending it to a new FormData Object. Unfortunately, the Blob is never appended.

I have a Nexus 5 running Android 5.1.1. I built an application using Cordova 5.1.1 and CordovaLib 4.0.2. I have the following plugins installed:

cordova-plugin-device cordova-plugin-inappbrowser cordova-plugin-camera cordova-plugin-file cordova-plugin-file-transfer cordova-plugin-whitelist

The same problem occurs with Cordova 4.3.1 & CordovaLib android 3.7.2.

Here is the snippet of code I use to activate the device camera.

navigator.camera.getPicture(
 onSuccess,
 onFailure, 
 {
   quality: 50,
   destinationType: Camera.DestinationType.DATA_URL,
   cameraDirection: Camera.Direction.FRONT
 }
);

Here are the success and failure functions.

function onSuccess(URI) {
  var the_file = new Blob([window.atob(URI)],  {type: 'image/jpeg', encoding: 'utf-8'});
  try {
    var fd = new FormData();
    fd.append('Filedata', the_file, "selfie.jpg");
  } catch (e) {
    throw e;
  }
  console.log(the_file.size);
  console.log(the_file.type);
  console.log(JSON.stringify(fd));
}
function onFailure(e) {
  console.log(e);
}

When I run this code, I monitor the console output in Android Studio. I can see that the_file.size and the_file.type are correct. However, the "JSON.stringify" of the FormData shows an empty Object "{}".

I understand that I should be using the FILE_URL instead of DATA_URL in Cordova to save memory, but I'm at the point now where I just need to debug this FormData issue. Once I get this example working, then I will refactor my code to use the FILE_URL before I put my app into production. I would like to focus on why the FormData.append function is not working for the Blob I'm creating.


Solution

  • After much more testing I have found that FormData does not work as expected in Cordova 5.1.1. I have also found inconsistencies with it in Cordova 4.2.3 and 5.0.

    The solution to working with File uploads from the Camera in cordova is to use the FileTransfer Object and the File system.

    Below is a modified onSuccess function that demonstrates how one would extract the image from the Camera and upload it to a server. FormData is never needed.

    onSuccess: function (URI) {
        window.resolveLocalFileSystemURL(URI, function (fileEntry) {
                console.log("Got URI");
                console.log(JSON.stringify(fileEntry));
                fileEntry.file(function (fileInput) {
                    console.log(fileInput.localURL);
                    var ft = new FileTransfer();
    
                    var options = new FileUploadOptions();
                    options.fileKey = "Filedata";
                    options.fileName = "image.jpg";
                    options.mimeType = "image/jpeg";
    
                    ft.upload(
                        fileInput.localURL,
                        encodeURI("http://www.posttestserver.com/post.php?dir=CordovaExample"),
                        function (r) {
                            console.log("Code = " + r.responseCode);
                            console.log("Response = " + r.response);
                            console.log("Sent = " + r.bytesSent);
                        },
                        function (e) {
                            alert("An error has occurred: Code = " + error.code);
                            console.log("upload error source " + error.source);
                            console.log("upload error target " + error.target);
                        },
                        options,
                        true
                    );
                }
        }
    }
    

    The important thing to point out is that we do not use the original URL sent to the onSuccess function from the Camera. We extract the actual File system URL from the FileEntry Object returned from the window.resolveLocalFileSystemURL function.

    Unfortunately, the issue with FormData is not documented anywhere I can find. I stumbled upon a bit touching on the URL vs localURL in the backward compatibility notes for the Cordova FileTransfer plugin here https://github.com/apache/cordova-plugin-file-transfer, at the bottom of the page.

    FileEntry.toURL() and DirectoryEntry.toURL() return a filesystem URL of the form cdvfile://localhost/persistent/path/to/file which can be used in place of the absolute file path in both download() and upload() methods.

    I also had trouble using FileReader to open a saved photo and append it to FormData. I believe there is an issue with the FormData API in the new Cordova. I'm also aware of API inconsistencies in past versions. My advice is to avoid using FormData and use the technique outlined above to get images out of Cordova's Camera or file system.