Cordova FileTransfer upload causes error code 3


#1

I’m unable to upload a photo to my server using Cordova’s file and file-transfer plugin through ionic. I’m able to succesfully capture the photo I need, and make a valid request to the server endpoint that’s expecting my image along with other data, but when I make the request, my payload is sent is as empty, which causes the server to respond with a 500 and ultimately an “error code 3” from the cordova plugin.

I’ve looked around and tried to figure this out, but seem to be stuck. Any help would be appreciated. I’ve tried this on both Android and iOS.

Here’s a snippet of my FileTransfer related code:

 $scope.send = function() {
    var myImg = $scope.picURL;

    var options = new FileUploadOptions();
    options.fileKey = "post";
    options.chunkedMode = false;
    var params = {};
    params.message = "HELLO";
    options.params = params;

    var ft = new FileTransfer();

    ft.upload(myImg, encodeURI(API_URL + "/uploadPhoto"), function(e){
          $ionicLoading.hide();
          alert("upload success!");
        },
        function(e){
          $ionicLoading.hide();
          alert("Upload failed!");
        }, options);
  }

Seems pretty basic, no?


#2

Could you tell the request payload for the post method?


#3

Isn’t the request payload already set as a POST method? I’m able to see the request to the “/uploadPhoto” endpoint reach there successfully on the server (which is a POST endpoint), but the data is empty.

If the “myImg” variable is null, shouldn’t the “options” at least be sent through?


#4

Have you checked that the payload contains the data (options.params)?

Can you share what is being sent as HAR (HTTP Archive format)?

Chrome > Developer Tools > Network > Select the request > Right click > Save as HAR with content .


#5

@joseadrian thanks for the response.

I’m not seeing the payload show up under Safari Web Inspector (iOS Simulator), which is strange because I see all other request payloads being captured correctly under the XHRs folder. In Chrome on the browser, I’m unable to test out this feature as it is dependent on the camera.

However, on my server, I have verified that the POST endpoint is reached and here are some snippets from the request payload (it’s an Express JS server). Hope this is useful in debugging:

body: {},
cookies: { 'connect.sid': 's:t9qzKwdvdVW3VoTiSbHNvwRsOo-6w-8a.gU4NpdEj8GYZjoLjYvpbUPhsQF7X1Ga+okk0Rj5E4aI' },
headers: { host: '**.*.***.***:3003',
 'x-requested-with': 'XMLHttpRequest',
 accept: '*/*',
 'content-type': 'multipart/form-data; boundary=+++++org.apache.cordova.formBoundary',
 'content-length': '509065',
 'accept-language': 'en-us',
 'accept-encoding': 'gzip, deflate',
 cookie: 'connect.sid=s%3At9qzKwdvdVW3VoTiSbHNvwRsOo-6w-8a.gU4NpdEj8GYZjoLjYvpbUPhsQF7X1Ga%2Bokk0Rj5E4aI',
 connection: 'keep-alive',
 'user-agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 10_9_5 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B411 (2093456832)' },

#6

You can inspect requests from Android using Chrome. Connect you phone. Go to chrome://inspect/#devices. Select your device.

But as you said, you are getting a empty body. There may be an error on FileUpload but I find that hard to believe.


#7

I stepped through the CDVFileTransfer.m class to see where it breaks, and it happens at the point where the Image File data is created, i.e. I would assume that the path is not valid? I’ve tried multiple ways to access the full path of the file, but perhaps I’m not accessing that correctly?

This is what my latest attempt looks like with regards to getting the ‘correct’ image uri:

window.resolveLocalFileSystemURL(fileUri, function(fileEntry){
      var name = fileEntry.fullPath.substr(fileEntry.fullPath.lastIndexOf('/') + 1);
      
      window.resolveLocalFileSystemURL(cordova.file.dataDirectory, function(fs) {
            fileEntry.copyTo(
                fs,
                name,
                function(entry){

                  var fullUrl = cordova.file.dataDirectory + entry.nativeURL.substr(entry.nativeURL.lastIndexOf('/') + 1);
              
                  $scope.picData = fullUrl;
                },
                onFail
            );
          },
          onFail);

    }, onFail);

#8

What is the value of $scope.picURL ? Are you using the Camera (Cordova) plugin? Is the destinationType option set as Camera.DestinationType.FILE_URI?

I’m working on an example.


#9

This is what I’ve done: Started a blank project and used FileTransfer and Camera plugins using ngCordova. Then:

.controller('DashCtrl', function($scope, $q, $cordovaCamera, $cordovaFileTransfer, $ionicActionSheet) {
  $scope.progress = 0;
  $scope.doSomething = function() {
    var deferred = $q.defer();

    var hideSheet = $ionicActionSheet.show({
      buttons: [
        { text: 'Take photo' },
        { text: 'Pick photo' }
      ],
      titleText: 'Do something',
      cancelText: 'Cancel',
      cancel: function() {
        deferred.reject();
      },
      buttonClicked: function(index) {
        deferred.resolve(index);
        return true;
      }
    });
    var options = {
      destinationType: Camera.DestinationType.FILE_URI
    };

    deferred.promise.then(function(index) {
      if(index == 0) {
        options.sourceType = Camera.PictureSourceType.CAMERA;
      } else {
        options.sourceType = Camera.PictureSourceType.PHOTOLIBRARY;
      }
      return $cordovaCamera.getPicture(options);
    }).then(function(imageURI) {
      return $cordovaFileTransfer.upload('http://requestb.in/1md5ql01', imageURI, {
        params: {
          framework: 'Ionic' // <<<<< This is sent
        }
      });
    }).then(function(result) {
      $scope.progress = 'SUCCESS!';
    }, function(err) {
      $scope.progress = 'FAIL!';
    }, function (progress) {
      $scope.progress = progress;
    });
  }

})

The demo and the requestb.in result:


#10

Thank you for the detailed example. That actually really helped me narrow it down to what the problem really was.

As I’m using Express 4x, multi-part form submissions are not supported by default, and this is why the request body is coming in as null!

Thanks again for your help.


#11

You can solve your problem? As?


#12

Same Problem testing the app on Android, code 3, body null exception: state 2
what am I missing?

  • I’ve checked the source and the target of the $cordovaFileTransfer.upload and they’re both correct.
  • In the config.xml I have the tag
  • I’ve added the options.headers = {Connection: “close”} to the FileUploadOptions

Testing the app on the ionic serve the error is not raised but the image is not uploaded.

Here’s the code:

var uploadPicture = function() {
        document.addEventListener('deviceready', function () {
            var fileURL = sessionStorage.getItem('profilePhotoURL');
            var fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
            sessionStorage.setItem('profilePhoto', fileName);
            var params = {};
            params.comesFrom = "profilePhoto";
            params.user = sessionStorage.getItem('usernamePhoto');
            var options = new FileUploadOptions();
            options.fileKey = "fileToSave";
            options.fileName = fileName;
            options.chunkedMode = false;
            options.headers = {
                Connection: "close"
            }
            options.mimeType = "image/jpeg";
            options.params = params;
            $cordovaFileTransfer.upload("http://tigertest.it/impliki/fileUploadParserJSON.php", fileURL, options).then(function(result) {
                var alertPopup = $ionicPopup.alert({
                    title: 'Ho caricato!',
                    template: 'Mi fermo qui?'
                });
                console.log("SUCCESS: " + JSON.stringify(result.response));
            }, function(err) {
                var alertPopup = $ionicPopup.alert({
                    title: 'Errore!',
                    template: JSON.stringify(err)
                });
                console.log("ERROR: " + JSON.stringify(err));
            }, function (progress) {
                var alertPopup = $ionicPopup.alert({
                    title: 'Sto qui!',
                    template: 'Mi fermo qui?'
                });
            });
        }, false);
    }

Thank you for your help!


#13

Nobody there? i’m still stuck on this problem!

I’ve removed and reinstalled the last version of cordova-plugin-file and cordova-plugin-file-transfer, I’ve tried to modify the upload call to match the criteria in the cordova documentation:

        ft.upload(fileURL, uri, win, fail, options);

but nothing changed! Still error 3…

Please, I’m goin’ out of my mind…

Thanks


#14

Are using the ionic proxy?
in my case,
this does NOT work:

var server = '/proxy/api/users/picture';

this does work:

var server = 'http://localhost:8000/api/users/picture';

#15

Hello everyone,
I’m trying to build an app with a Capture and FileTransfer plugins.
My Capture feature works well so I’m able to record videos but when I try to send them to my server it doesn’t work (error code 3) and I can’t figure out why.
Any help would be appreciate. Here’s my test controller :

app.controller('CaptureCtrl', function($scope, $cordovaCapture, $timeout, $cordovaFileTransfer){
        // Video capture
        $scope.captureVideo = function() {
          var options = { limit: 1, duration: 69 };
          $cordovaCapture.captureVideo(options).then(function(videoData) {
            // Success
            $scope.name = videoData[0].name;
            $scope.url = videoData[0].fullPath; //.replace("file:","");
            $scope.type = videoData[0].type;

            // Upload to server
            $cordovaFileTransfer.upload("https://www.tripnlive.com/videos/upload.php", videoData[0].fullPath, {mimeType: "video/mp4", fileName:"test"}, true)
              .then(function(result) {
                // Success!
                $scope.upload = "success";
              }, function(err) {
                // Error
                $scope.upload = "error code: "+err.code;
              }, function (progress) {
                // constant progress updates
                $scope.progress = progress.loaded / progress.total * 100;
              });

          }, function(err) {
            // Error
          });
        }

    });

Thanks!


#16

Very curious at your server code if you’re using Node.js. My app works well in a php server. But doesn’t work in a Node.js server.


#17

Hi,
Did u find the solution? Im also facing the same issue…On WIFI it is working fine but on mobile data it is throwing error.

Thankyou


#18

Hi Karthik123,

here’s my upload video function. It worked. I recommend you the Connection: close option and the event listener deviceready. Hope it works also for you.

S

function uploadVideo() {
    document.addEventListener('deviceready', function () {
        var fileURL = sessionStorage.getItem('lastVideoUploaded');
            var alertPopup = $ionicPopup.alert({
                title: 'Video about to upload!',
                template: fileURL
            });
        var fileName = fileURL.substr(fileURL.lastIndexOf('/') + 1);
        if(fileURL.substring(0, 4) == "http") {
            fileURL = encodeURI(fileURL);
        }
        sessionStorage.setItem('profilePhoto', fileName);

        function win(r) {
            var alertPopup = $ionicPopup.alert({
                title: 'Video Uploaded!',
                template: "Don't worry, you can always change it! Now save your profile!"
            });
            $state.go('tab.UMP');
        }

        function fail(error) {
            var alertPopup = $ionicPopup.alert({
                title: 'Errore!',
                template: JSON.stringify(error)
            });
        }

        var uri = encodeURI("http://yourpage.php");

        var params = {};
        params.comesFrom = "challenge";
        params.user = $scope.data.userCompleto;
        var options = new FileUploadOptions();
        options.fileKey = "fileToSave";
        options.fileName = fileName;
        options.chunkedMode = false;
        options.headers = {
            Connection: "close"
        }
        options.mimeType = "video/mpeg"; //retrieve the correct mime type
        options.params = params;


        var ft = new FileTransfer();
        ft.onprogress = function(progressEvent) {
            if (progressEvent.lengthComputable) {
              loadingStatus.setPercentage(progressEvent.loaded / progressEvent.total);
            } else {
              loadingStatus.increment();
            }
        };
        ft.upload(fileURL, uri, win, fail, options);
    }, false);
}

#19

@zhouhaowowtv Did you find any working version of server code for node.js?


#20

For those who need server implementation in node for uploading the file

App
cordova file transfer API call in the app

$cordovaFileTransfer.upload("http://some uri/upload", imageUrlToUpload, options)
        .then(function(result) {
            // Success!
            console.log('upload success');
        }, function(err) {
            // Error
            console.log("error in file upload");
            console.log(err);
        }, function (progress) {
            // constant progress updates
            console.log('some progress')
        });

Node Server code

var multer  =   require('multer');

var storage = multer.diskStorage({
    destination: function (req, file, callBack) {
        callBack(null, 'destinationFolder');
    },

you can also pass in folder name in params to the destination function from the app and access it using req.body
make sure that folder already exists and then pass it to the callBack
you can use normal file functions to check whether folder exists and create it if it doesn’t exist and then pass it to the callBack function

    filename: function (req, file, callBack) {
        callBack(null, 'someName');
    }
});

 
var upload = multer({ storage: storage }).single('file');

// single(‘file’) - this is the filekey in the options parameter passed in cordova-file-transfer call from app

 app.post('/upload', function (req, res) {
     upload(req, res, function(err) {
         if(err) {
             return res.end("Error uploading file.");
         }
         res.end("File is uploaded");
     });
});