Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to use generateInternalStream? #522

Open
FPG-Alan opened this issue May 8, 2018 · 2 comments
Open

how to use generateInternalStream? #522

FPG-Alan opened this issue May 8, 2018 · 2 comments

Comments

@FPG-Alan
Copy link

FPG-Alan commented May 8, 2018

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!

@zipzapduo
Copy link

I have the same problem here. did you solve?

@lajtomekadimon
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants