Phonegap File Transfer Error Code 3 [solved]

Phonegap has proven itself as a perfect platform for rapid cross-platform application development for iOS (iPhone, iPad), Android and other mobile devices. It allows to significantly save time and budget which is important for start-ups as well as for established companies willing to do first steps on mobile market services.

Just as any other advanced framework it includes plenty of features built-in. Although most of them are well documented and work just as they are supposed to, some tasks bring you challenges that are not always easy to solve.

Stack Overflow, Google Groups and other technical boards are full of questions and solutions. Sometimes, however, it is not so easy to find a working solution to a pretty common issue, and this article solves one of them.

While working on a Phonegap application that features an image upload to a server we have faced an issue on Android version: every second file upload process resulted in an error. What’s interesting is that it was network error while application didn’t even try to connect to the server and on first time the file was uploaded successfully, next time – error, again – good, next – error.

Having researched on the web we’ve found that this issue is pretty common and had been noticed starting from very old Phonegap versions and ending with most recent Phonegap version 3. A number of bugs are filed in Cordova bug tracker and still they don’t bring any help.

If you use a standard code like:

{
   "code":3,
   "source":"file:///storage/sdcard0/VK/QFveWpLr4Hw.jpg",
   "target":"http://XXX/api/actions?action=uploadresponse",
   "http_status":null,
   "body":null
}

That brings you to the following error:
Screenshot_2013-10-09-10-35-21

While it works perfectly fine on iOS, Android gives you error=3 every second time.

Solution we found is pretty simple. Add line:

CoffeeScript

@op.headers = {
          Connection: "close"
        }

JavaScript

        this.op.headers = {
          Connection: "close"
        };

To the code:

CoffeeScript

uploadPhoto: (imageURI) ->
      utils.preloaderStart()
      try
        #init options for upload
        @op = new FileUploadOptions()
        @op.fileKey = "qqfile"
        @op.fileName = imageURI.substr(imageURI.lastIndexOf('/')+1)
        @op.mimeType = "image/jpeg"
        @op.chunkedMode = false
        @op.headers = {
          Connection: "close"
        }

        #init uploader
        @ft = new FileTransfer()

        #progressbar
        @ft.onprogress = (progressEvent) ->
          if progressEvent.lengthComputable
            perc = Math.floor(progressEvent.loaded / progressEvent.total * 100)
            utils.showProgressBar(perc)

        #upload photo
        if window.Device.android
          utils.log "upload file from Android"
          #get real path of file
          window.resolveLocalFileSystemURI imageURI, (fileEntry) =>
            fileEntry.file (fileObj) =>
              fileName = fileObj.fullPath
              @op.fileName = fileName.substr(fileName.lastIndexOf('/')+1)
              @ft.upload(fileName, window.baseUrl+'api/actions?action=uploadresponse', @onUploadPhotoSuccess, @onUploadPhotoFail, @op, true)
        else
          utils.log "upload file from other device"
          @ft.upload(imageURI, window.baseUrl+'api/actions?action=uploadresponse', @onUploadPhotoSuccess, @onUploadPhotoFail, @op, true)

      catch e
        utils.log e

    onUploadPhotoSuccess: (r) =>
      utils.preloaderStop()
      utils.hideProgressBar()
      utils.log r
      ###
      some code
      ###

    onUploadPhotoFail: (error) =>
      utils.hideProgressBar()
      utils.preloaderStop()
      utils.log error
      utils.alert("An error has occurred: Code = " + error.code)

JavaScript

MainMenuView.prototype.uploadPhoto = function(imageURI) {
      var e,
        _this = this;

      utils.preloaderStart();
      try {
        this.op = new FileUploadOptions();
        this.op.fileKey = "qqfile";
        this.op.fileName = imageURI.substr(imageURI.lastIndexOf('/') + 1);
        this.op.mimeType = "image/jpeg";
        this.op.chunkedMode = false;
        this.op.headers = {
          Connection: "close"
        };
        this.ft = new FileTransfer();
        this.ft.onprogress = function(progressEvent) {
          var perc;

          if (progressEvent.lengthComputable) {
            perc = Math.floor(progressEvent.loaded / progressEvent.total * 100);
            return utils.showProgressBar(perc);
          }
        };
        if (window.Device.android) {
          utils.log("upload file from Android");
          return window.resolveLocalFileSystemURI(imageURI, function(fileEntry) {
            return fileEntry.file(function(fileObj) {
              var fileName;

              fileName = fileObj.fullPath;
              _this.op.fileName = fileName.substr(fileName.lastIndexOf('/') + 1);
              return _this.ft.upload(fileName, window.baseUrl + 'api/actions?action=uploadresponse', _this.onUploadPhotoSuccess, _this.onUploadPhotoFail, _this.op, true);
            });
          });
        } else {
          utils.log("upload file from other device");
          return this.ft.upload(imageURI, window.baseUrl + 'api/actions?action=uploadresponse', this.onUploadPhotoSuccess, this.onUploadPhotoFail, this.op, true);
        }
      } catch (_error) {
        e = _error;
        return utils.log(e);
      }
    };

    MainMenuView.prototype.onUploadPhotoSuccess = function(r) {
      utils.preloaderStop();
      utils.hideProgressBar();
      return utils.log(r);
    };

    /*
    some code
    */

    MainMenuView.prototype.onUploadPhotoFail = function(error) {
      utils.hideProgressBar();
      utils.preloaderStop();
      utils.log(error);
      return utils.alert("An error has occurred: Code = " + error.code);
    };

    return MainMenuView;

 

This is it. Works!  Sometimes you have to spend many hours during research to find a solution dead-simple. Hopefully this article saves you some time, fellow Phonegap App developers!

Will appreciate your feedback in comments: Did it help? Did you find any other solutions? Any ideas what’s causing this?