0

I've got code that reads a local folder, then zips up relevant files and uploads them to a server:

var dirEntry = fileSystem.createReader();

dirEntry.readEntries(
    function (entries) {
        if (entries.length === 0)
            return;

        var options = new FileUploadOptions();

        /* Details omitted */

        var zip = new JSZip();

        // Map function from Ramda library
        R.map(function (entry) {
            var fileName = entry.name;

            // Check that this is a file, and it's not a zip file.
            if (entry.isFile && (fileName.indexOf(".zip", fileName.length - 4) == -1)) {
                return entry.file(function (file) {

                    var reader = new FileReader();
                    reader.onloadend = function (evt) {
                        // Add the file to the Zip
                        zip.file(fileName, evt.target.result);
                    };
                    reader.readAsArrayBuffer(file);

                }, function (error) { // ignore });
            }

            return null;
        }, entries);

        fileSystem.getFile(options.fileName, { create: true, exclusive: false },
            function (fileEntry) {
                fileEntry.createWriter(function (writer) {
                    // Generate the binary Zip file
                    var content = zip.generate({ type: "blob", compression: "DEFLATE" });

                    // Upload the file after it's persisted to storage
                    writer.onwriteend = function (evt) {
                        var ft = new FileTransfer();
                        ft.upload(fileEntry.nativeURL, 
                            UPLOAD_SERVER_URL,
                            function (data) { // Ignore },
                            function (error) { // Ignore }, 
                            options)
                    };

                    // Persist the zip file to storage
                    writer.write(content);

                }, function (error) {
                    // Ignore
                });
            }, function (error) {
                // Ignore
            });

        }
, errorFunction);

The problem is that sometimes an empty file is uploaded, which should never happen. So it's probably a synchronization issue that's causing the file to upload before the Zip file is created and written to storage. I'm not sure where the problem is though. I've tried returning Promises from the Map function, then using the Promise.all(...).then() pattern but sometimes I still get a blank file. So either my implementation was incorrect, or zip.generate() also runs asynchronously (or both).

How can I ensure that the zip file is fully written to storage before attempting to upload it?

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
ilitirit
  • 16,016
  • 18
  • 72
  • 111

1 Answers1

0

The FileReader API is asynchronous : when you create your writer, the reader can be still waiting (and the zip file still empty). Promises are the right pattern here, just be sure to resolve the promise after adding the file to zip :

var promises = R.map(function (entry) {
  // ...
  var deferred = Q.defer(); //  <= create here
  reader.onloadend = function (evt) {
    zip.file(fileName, evt.target.result);
    deferred.resolve(text); // <= resolve here
    // call deferred.reject when you encounter an error
  }
  reader.readAsArrayBuffer(file);

  return deferred.promise;
}, entries);

// then, Promise.all(...).then()

That way, the promise will resolve only after the content is added to zip.

David Duponchel
  • 3,959
  • 3
  • 28
  • 36