Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to add multiple files to a zip with zip.js?

Tags:

javascript

zip

I am using the javascript zip.js library. I've seach all around I a cannot find an example where more than one file is added to the zip.

Here is my code, but it generates a "corrupted" zip.

var len = results.rows.length, i;
var k=1;
zip.createWriter(new zip.BlobWriter(), function(writer) {
    for (i = 0; i < len; i++){
        // get the image url from a sqlite request
        url = results.rows.item(i).url;


        var img = new Image();
        img.onload = function() {
            var a = document.createElement('a');
            a.href = this.src;
            var filename= a.pathname.split('/').pop(); // filename.php
            timest = new Date().getTime();
            // use a TextReader to read the String to add

                writer.add(timest+".jpg", new zip.Data64URIReader(getBase64Image(img)), function() {
                // onsuccess callback
                    k++;
                    if(k==len){
                        setTimeout(function(){
                        writer.close(function(blob) {

                            // blob contains the zip file as a Blob object
                            $('#test').attr("href", window.URL.createObjectURL(blob));
                            $('#test').attr("download", "woeii.zip");

                        });
                        },1000);
                    }
                }, function(currentIndex, totalIndex) {
                // onprogress callback
                });



        };
        img.src = url;
    }
});

Any idea to make it work? :)

like image 756
Paul Fournel Avatar asked Jul 07 '13 09:07

Paul Fournel


2 Answers

If you are looking for a good example of code that handles multiple files, see here. You can then view the source code.

This is the key source of the demo (modified just slightly):

var obj = this;
var model = (function() {
    var zipFileEntry, zipWriter, writer, creationMethod, URL = obj.webkitURL || obj.mozURL || obj.URL;

    return {
        setCreationMethod : function(method) {
            creationMethod = method;
        },
        addFiles : function addFiles(files, oninit, onadd, onprogress, onend) {
            var addIndex = 0;

            function nextFile() {
                var file = files[addIndex];
                onadd(file);
                // Modified here to use the Data64URIReader instead of BlobReader
                zipWriter.add(file.name, new zip.Data64URIReader(file.data), function() {
                    addIndex++;
                    if (addIndex < files.length)
                        nextFile();
                    else
                        onend();
                }, onprogress);
            }

            function createZipWriter() {
                zip.createWriter(writer, function(writer) {
                    zipWriter = writer;
                    oninit();
                    nextFile();
                }, onerror);
            }

            if (zipWriter)
                nextFile();
            else if (creationMethod == "Blob") {
                writer = new zip.BlobWriter();
                createZipWriter();
            } else {
                createTempFile(function(fileEntry) {
                    zipFileEntry = fileEntry;
                    writer = new zip.FileWriter(zipFileEntry);
                    createZipWriter();
                });
            }
        },
        getBlobURL : function(callback) {
            zipWriter.close(function(blob) {
                var blobURL = creationMethod == "Blob" ? URL.createObjectURL(blob) : zipFileEntry.toURL();
                callback(blobURL);
                zipWriter = null;
            });
        },
        getBlob : function(callback) {
            zipWriter.close(callback);
        }
    };
})();

Usage: Assumes a <a id="downloadLink">Download</a> element exists to provide the download once ready.

// Prepare your images
var files = [];
for (i = 0; i < len; i++) {

    // Get the image URL from a SQLite request
    var url = results.rows.item(i).url;

    (function(url){
        var img = new Image();
        img.onload = function() {
            // Add to file array [{name, data}]
            var a = document.createElement('a');
            a.href = this.src;
            var filename= a.pathname.split('/').pop();

            console.log("Loaded file " + filename);
            files.push({name: filename, data: getBase64Image(img) });
        }
        img.src = url;
    })(url);
}

// Wait for the image to load
var check = setInterval(function(){
    if(files.length==images.length) {
        clearInterval(check);

        // Set the mode
        model.setCreationMethod("Blob");

        // Add the files to the zip
        model.addFiles(files, 
            function() {
                // Initialise Method
                console.log("Initialise");
            }, function(file) {
                // OnAdd
                console.log("Added file");
            }, function(current, total) {
                // OnProgress
                console.log("%s %s", current, total);
            }, function() {
                // OnEnd
                // The zip is ready prepare download link
                // <a id="downloadLink" href="blob:url">Download Zip</a>
                model.getBlobURL(function(url) {
                    document.getElementById("downloadLink").href = url;
                    document.getElementById("downloadLink").style.display = "block";
                    document.getElementById("downloadLink").download = "filename.zip";
                });
            });

    }
}, 500);

You can use the example source code to add in progress indicators. Hope this helps, the nice thing about this method is the zip model is easily reusable if you make it it's own JS file.


Another thought: I presume you are using the getBase64Image function from here, if so and you still experience corruption issues, perhaps try modifying the return to simply return dataURL; and comment out the .replace(..., as the Data64URIReader may expect the prefix.

like image 78
Scott Avatar answered Nov 20 '22 05:11

Scott


Here's a stripped-down version of that demo that only uses RAM storage. It assumes that zip.js, z-worker.js, and deflate.js from the zip.js install are in the same directory as the two files below, along with FileSaver.js.

Note: This is not production-ready code! It is a bare-bones demo that I made so I could figure out what was going on. If you generate and save the zip programmatically, you may need to implement a nextFile() iterator like the one above to prevent a race condition from populating the zip with empty files. (See https://stackoverflow.com/a/29738675/738675 for an example of this.)

demo.html:

<li>
    add files into the zip
    <input type="file" multiple id="file-input" onchange="addFiles(this.files)">
</li>
<li>
    download the zip file
    <a href="#" onclick="saveZip()">Download</a>
</li>

<script type="text/javascript" src="zip.js"></script>
<script type="text/javascript" src="demo.js"></script>
<script type="text/javascript" src="FileSaver.js"></script>

demo.js:

var zipWriter;

function addFiles(files) {
    writer = new zip.BlobWriter();
    zip.createWriter(writer, function(writer) {
        zipWriter = writer;
        for (var f = 0; f < files.length; f++) {
            zipWriter.add(files[f].name,
            new zip.BlobReader(files[f]), function() {});
        }
    });
}

function saveZip() {
    zipWriter.close(function(blob) {
        saveAs(blob, "Example.zip"); // uses FileSaver.js
        document.getElementById("file-input").value = null; // reset input file list
        zipWriter = null;
    });
}
like image 32
meetar Avatar answered Nov 20 '22 06:11

meetar