jszip icon indicating copy to clipboard operation
jszip copied to clipboard

how to use generateInternalStream?

Open FPG-Alan opened this issue 7 years ago • 2 comments

Hi, I want to use jszip in browser with some large file. For now I use generateInternalStream and write zip data to chrome sandbox file system, then trigger a click event to download the zip file, however, the generated zip file seems broken.

here is the code:

window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(window.TEMPORARY, 5000 * 1024 * 1024, (fs) => {
    fs.root.getFile('creatives.zip', { create: true }, fileEntry => {
        fileEntry.createWriter(writer => {
            ziper.generateInternalStream({ type: 'blob', streamFiles: true }).on('data', function (data, info) {
                if (info.currentFile && info.percent !== 100) {
                    this.pause()
                    writer.seek(writer.length)
                    writer.write(new Blob([data]))
                    writer.onwriteend = (e) => {
                        this.resume()
                    }
                } else {
                    var a = document.createElement("a"),url = fileEntry.toURL()
                    a.href = url;
                    document.body.appendChild(a);
                    a.click();
                    console.log(fileEntry)

                    setTimeout(() => {
                        document.body.removeChild(a);
                        fileEntry.remove(function () {
                            console.log('File removed.');
                        }, (error) => { });
                    }, 2000);
                }
            })
        })
    })
})

Thank you!

FPG-Alan avatar May 08 '18 07:05 FPG-Alan

I have the same problem here. did you solve?

zipzapduo avatar Aug 31 '18 14:08 zipzapduo

We made it using TypeScript and Cordova for an Android app:

async function writeZipFile(dirEntry: DirectoryEntry, zip: JSZip, fileName: string): Promise<void> {
    return new Promise((ok, err) => {
        dirEntry.getFile(
            fileName,
            { create: true, exclusive: false },
            function (fileEntry: FileEntry) {
                fileEntry.createWriter((fileWriter: FileWriter) => {
                    let chunksToAdd: Blob[] = [];
                    let flushWriteHandler: ReturnType<typeof setTimeout> = null;

                    const stream = zip.generateInternalStream({
                        type: "blob",
                        compression: "DEFLATE",
                        compressionOptions: {
                            // 1: Best speed
                            // ...
                            // 9: Best compression
                            level: 5,
                        },
                        streamFiles: true,
                    });

                    fileWriter.onwrite = () => {
                        stream.resume();
                    };
                    fileWriter.onerror = (e: ProgressEvent<EventTarget>) => {
                        console.error(`Failed zip file write: ${e.toString()}`);
                        // Here you should delete the file
                    };

                    stream.on("data", (chunk: Blob, _metadata: JSZip.JSZipMetadata) => {
                        stream.pause();
                        chunksToAdd.push(chunk);
                        if (!flushWriteHandler) {
                            flushWriteHandler = setTimeout(() => {
                                if (chunksToAdd.length === 0) {
                                    return;
                                }
                                const blobToAdd = new Blob(chunksToAdd);
                                chunksToAdd = [];
                                flushWriteHandler = null;
                                fileWriter.write(blobToAdd);
                            }, 0);
                        }
                    });
                    stream.on("end", () => {
                        function finishIfReady() {
                            if (fileWriter.readyState === FileWriter.DONE) {
                                ok();
                            } else {
                                setTimeout(finishIfReady, 100);
                            }
                        }
                        setTimeout(finishIfReady, 0);
                    });
                    stream.on("error", (e: Error) => {
                        stream.pause();
                        console.error(`Failed zip stream: ${e.toString()}`);
                        // Here you should delete the file
                    });

                    stream.resume();
                });
            },
            function (error: FileError) {
                err(error);
            }
        );
    });
}

// Use
await writeZipFile(dirEntry, zip, fileName);

Our issue was that fileWriter.write() and stream.on("data") callback couldn't be sync due to Cordova's FileWriter nature; Java does the write, while JavaScript just sends the order to write, so you can only wait for fileWriter.onwrite callback, but the JSZip stream keeps going on.

So to force an order when adding data to the file chunk by chunk, setTimeout() did the job. We queue up each write and pause the stream until the current set of chunks has already been added.

I'm not sure if this is the case for browser, but may be similar.

lajtobw avatar Jul 28 '22 08:07 lajtobw