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

Fix file download in HTTP over non secure context #308

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

SaiyanRiku
Copy link

@SaiyanRiku SaiyanRiku commented Mar 10, 2023

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();
    });
}

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

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
Copy link
Owner

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
Copy link
Author

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.

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

Successfully merging this pull request may close these issues.

None yet

2 participants