jimmywarting / StreamSaver.js

StreamSaver writes stream to the filesystem directly asynchronous
https://jimmywarting.github.io/StreamSaver.js/example.html
MIT License
3.95k stars 413 forks source link

Fix file download in HTTP over non secure context #308

Open SaiyanRiku opened 1 year ago

SaiyanRiku commented 1 year ago

Hello,

I want to propose this modification that solve a download problem in my usage in mode HTTP in non secure context.

By default, the following code works if hostname matches http://localhost:3000/ or https://mydomain.com:3000, but not if using http://mydomain.com:3000 or http://192.168.1.1:3000.

I'm using streamSaver with react and npm version 2.0.6, with this function.

export function fetchFile(url, opts, filename) {

    // Use https://developer.mozilla.org/en-US/docs/Web/API/FileSystemWritableFileStream in the future
    if(opts.streamSaverPath){
        streamSaver.mitm = opts.streamSaverPath + '/mitm.html';
        console.log("Using mitm: " + streamSaver.mitm);
    }

    return fetch(url, opts).then(response => {
        const totalSize = response.headers.get('Content-Length');
        console.log("File size is: " + totalSize);

        // If the WritableStream is not available (Firefox, Safari), take it from the ponyfill
        if (!window.WritableStream) {
            console.log("Using ponyfill WritableStream");
            streamSaver.WritableStream = WritableStream;
            window.WritableStream = WritableStream;
        }

        const fileStream = streamSaver.createWriteStream(filename, {size: totalSize});
        const readableStream = response.body;

        // More optimized (mainly available in Safari)
        if (readableStream.pipeTo) {
            console.log("Using pipeTo method");
            return readableStream.pipeTo(fileStream);
        }

        console.log("Using pump method");

        // Else we create manually the pipe
        const writer = fileStream.getWriter();

        const reader = readableStream.getReader();
        const pump = () => reader.read()
            .then(res =>
                res.done ? writer.close() : writer.write(res.value).then(pump)
            );

        return pump();
    });
}
jimmywarting commented 1 year ago

Hmm 🤔 seems to go a bit against my popup hack proposal. the blob fallback is the least favored method of saving data where it will store everything in memory until it can be saved.

the http solution is to create a popup window that can install a service worker via a secure domain. once the service worker is installed will it then transfer the readable stream over to the service worker and then close the popup window.

SaiyanRiku commented 1 year ago

OK i understand well ! But in my case, it doesn't work. The popup open well, i can see in the browser inspector (Firefox) that the file seem to be downloaded, but it doesn't appears in the downloads list or in my download directory. But it works with the blob fallback or the iframe method in secure context. I was not able to find why the popup method doesn't works. Is there something i can do to help ?

I succeed to reproduce the bug on Firefox and Chrome on Linux and Windows, i don't try other browser.

Another workaround, in my case will be to add an option to disable the popup method, but certainly it would be better to fix the popup method.