Link to home
Start Free TrialLog in
Avatar of ank5
ank5Flag for India

asked on

upload zip file from Ionic to NodeJS

From my Ionic app, running on iPad, I need to upload a zip file to Node Express.

Below is my code for saving the zip on iPad and posting it to the Express server

//This creates the zip file locally. Zip creation on iPad works fine as I am able to open it.
//Using JSZip to create a zip file. Type is set to blob
var zipFiles = function(filesList, uniqueFilename) {
            var zip = new JSZip();
            var zipFilesPromises = filesList.map(function (file) {
               return $cordovaFile.readAsBinaryString(cordova.file.dataDirectory + $rootScope.username, file)
              .then(function (binaryData) {
                 return zip.file(file, binaryData, { binary: true });
              });
            }); 

            return Promise.all(zipFilesPromises)
                .then(function () {
                    return zip.generateAsync({ type: "blob" });
                })
                .then(function (blob) {
                    return $cordovaFile.writeFile(cordova.file.dataDirectory + $rootScope.username, uniqueFilename, blob, true);
                })
                .then(function (data) {
                    return Promise.resolve(data);
                })
                .catch(function (error) {
                    console.log('Error while zipping and writing ' + JSON.stringify(error));
                }) 
        }

Open in new window


This function is then called from the controller. Upon zip file creation it sends it to the Node server using $cordovaFileTransfer.

   
 return fileAPI.zipFiles(docList, zipFilename) 
                    }).then(function(data) {
                        var targetPath = cordova.file.dataDirectory + $rootScope.username+ '/'+zipFilename;
                        var trustHosts = true;
                        var options = {};
                        var server = config.nodeServerEndpoint + '/uploadparcel';
                        return $cordovaFileTransfer.upload(server, targetPath, options, trustHosts);
                    }).then(function (result) {
                        console.log("success", result);
                    }).catch(function(error) {
                        console.log('Error - '+JSON.stringify(error));
                    });

Open in new window


On the Node server side, below code is saving the zip file

function uploadZip(request, response) {
    var size = 0;
    var data = new Buffer('');
    request.on('data', function (chunk) {
        data = Buffer.concat([data, chunk]);
    });
    request.on('end', function () {
        request.rawBody = data;
        fs.writeFile('userdata/abc123.zip', request.rawBody, 'binary', function(err){
           if (err) {
              throw err;
           }
        })
    response.end("Thanks");
    });
    request.on('error', function(e) {
        console.log("ERROR ERROR: " + e.message);
    });
}

Open in new window


This code creates a corrupted zip file when called from ionic app. However, if I attach a file in Postman and post to this URL then a valid zip file is created.

So, I suspect it could be something to do with how I am creating my zip file on Ionic (as a blob) and seems that node code is not able to understand that.

In nutshell, zip being created in the device is valid (as I am able to open it) and the zip saved on the node server is also valid when called from Postman. However, when zip created in ionic is send to node, then node saves a corrupted zip file.

Is there something else that I need to be doing on either the node or the ionic side to make it work.
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

If you compare the two files - does this shed any light?
Avatar of ank5

ASKER

When I try to unzip the file created on node (though Ionic), it gives the following message

unzip userdata/abc123.zip
Archive:  userdata/abc123.zip
  End-of-central-directory signature not found.  Either this file is not
  a zipfile, or it constitutes one disk of a multi-part archive.  In the
  latter case the central directory and zipfile comment will be found on
  the last disk(s) of this archive.
unzip:  cannot find zipfile directory in one of userdata/abc123.zip or
        userdata/abc123.zip.zip, and cannot find userdata/abc123.zip.ZIP, period.

Open in new window


Also, it's much smaller in size than the file that is created on Node through Postman.
I would do a binary compare of the two files to see what is different - it might give some clue as to what / how the file is being corrupted.
Avatar of ank5

ASKER

You are right, binary comparison helped. But still don't know how to resolve the problem.

It seems that $cordovaFileTransfer plugin is adding some bytes to the zip file. When I try to unzip the file which is uploaded by $cordovaFileTransfer plugin, it gives me an error message stating

warning[abc.zip]: 172 extra bytes at the beginning or within zip file

Open in new window


Then I did a binary comparison of the zip file send through $cordovaFileTransfer (not working) and the one send through postman (working). Found that the one send through $cordovaFileTransfer has the following in the beginning of the zip file (which is making it invalid).

--+++++org.apache.cordova.formBoundary
Content-Disposition: form-data; name="file"; filename="image.jpg"
Content-Type: application/octet-stream
Content-Length: 22705

Open in new window


All the content is also there as a part of the (not working) zip, but these additional bytes in the beginning are making it invalid.

Here is my $cordovaFileTransfer code. I guess, it’s options that is causing the problem

return fileAPI.saveDataInFs(JSON.stringify(uploadJson), jsonFilename)
     .then(function(data) {
        return fileAPI.zipFiles(docList, zipFilename) 
     }).then(function(data) {         
         var targetPath = cordova.file.dataDirectory + $rootScope.username+ '/'+jsonFilename;
         var trustHosts = true;
         var options = {
           mimeType:"application/octet-stream"
         };
         var server = config.nodeServerEndpoint + '/upload';
         return $cordovaFileTransfer.upload(server, targetPath, options, trustHosts);
     }).then(function (result) {
         console.log("success", result);
     }).catch(function(error) {
         console.log('Error - '+JSON.stringify(error));
     }); 

Open in new window


In my use case, user does not select the file that needs to be uploaded. Based on a user action (like button click), application would look for a zip file in a specific location on iPad and upload it using $cordovaFileTransfer.
ASKER CERTIFIED SOLUTION
Avatar of Julian Hansen
Julian Hansen
Flag of South Africa image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial