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

Event to indicate download started and/or finished #954

Closed
sicking opened this issue Mar 29, 2016 · 9 comments
Closed

Event to indicate download started and/or finished #954

sicking opened this issue Mar 29, 2016 · 9 comments
Labels
addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest

Comments

@sicking
Copy link

sicking commented Mar 29, 2016

The best way to enable a website to save a Blob or File is by doing something like:

<a id=downloader href="" download>

and

var url = URL.createObjectURL(myBlob);
var link = document.getElementById('downloader');
link.href = url;
link.click(); // Or show in UI and wait for user to click

The problem is that there's no good time to revoke the object URL. The problem is that browsers tend to start the download asynchronously after the link has been clicked. This is especially browsers which show the user a "do you want to open or download this file?" dialog, where the download might start long after the link was clicked.

Per the Blob spec, it should be fine to call URL.revokeObjectURL(url) as soon as the download has started, but there's no way to know when that is.

We should fire an event either when the download actually starts, or when it finishes. This could also be useful to allow the webpage to hide the download link once the user actually starts downloading, rather than when they click the link.

@annevk
Copy link
Member

annevk commented Mar 29, 2016

Actually, per the specifications, cloning of the blob should happen once you assign the URL in link.href = url. So you should theoretically be able to revoke it then.

@sicking
Copy link
Author

sicking commented Mar 29, 2016

I thought that cloning happened once loading starts? Is that different for <a>? Or for a larger set of features? Where is this defined?

@annevk
Copy link
Member

annevk commented Mar 29, 2016

Cloning happens during parsing: https://url.spec.whatwg.org/#concept-url-parser. And parsing happens directly. That's the only deterministic model we could come up with.

@sicking
Copy link
Author

sicking commented Mar 29, 2016

Ok. If this is something browser vendors has agreed is implementable then we can close this request (unless we want to keep it open to satisfy the "remove UI when download starts" use case). Though so far it doesn't seem like browsers have implemented?

sicking referenced this issue in eligrey/FileSaver.js Mar 29, 2016
This solution still isn't technically correct because it could take more
than 40 seconds to download a file. I could simply not revoke any files,
but then users would run into issues once they have saved a cumulative
total of over 500MB (
https://bugs.chromium.org/p/chromium/issues/detail?id=375297 )

Complain to @sicking and @arunranga for creating and standardizing an
incomplete revocation API.
@annevk
Copy link
Member

annevk commented Mar 29, 2016

Yeah, I don't think browsers have implemented the deterministic model. I hope they will since determinism is better, doh, but if they don't we need a redesign, which will be sad.

Irrespective of blobs it might still make sense to have start/finish events for downloading, to display "waiting UI". I don't know how much privacy concerns we have with that, potentially it's not great for cross-origin resources?

@eligrey
Copy link

eligrey commented Mar 29, 2016

potentially it's not great for cross-origin resources?

Although I agree that ideally download start/finish events should be same-origin (and CORS)-only, timing information for cross-origin resource loading is already exposed via <img onload=... onerror=...> (not sure if this has been changed, please correct me if I'm wrong), so it wouldn't make much practical sense to limit the events to same-origin resources. Whether or not it's same-origin limited doesn't matter that much to me though, as I only need this event for same-origin blob object URLs.

@jimmywarting
Copy link

jimmywarting commented May 23, 2016

How about some progress event also?


Little of track here but:

I had one idea to be able to skip creating/revoking url all together using service worker, fetch, a[download], respondWith() and last but not least ReadableByteStream

// <a href="intercept-me" download="sample.zip">download sample.zip</a>

// service worker
self.addEventListener("fetch", evt => {

    if( evt.request.url.indexOf('intercept-me') ){
        let stream = new ReadableByteStream()
        let res = new Response(stream, {
            headers: {
                // Specify a filename
                'Content-Disposition': 'attachment; filename=sample.zip',
            }
        })
        // Replace download link with a readable stream
        evt.respondWith(res)

        // do something with stream (generate a 2GB large zip file)
    }

})

This way you could create a async saving stream and be able to save much larger file
You would even be able to specify the path where you want to save a file even before it has started to generate a full result
so this would consume less memory since it could have a high water mark
You would know how much has been transferred and you know when it starts/ends

Now ReadableByteStream isn't available yet so it wouldn't work. So i tried emulating 206 partial download but was unsuccessful.

This would require a service worker to intercept a[download] link that means it has to be in https (SSL)
It sure is a lot of hassle of creating a async stream that you could use for pipeing something to the filesystem

Tried creating a objectUrl from a audio stream first to use with a[download] but it didn't work.
It would definitely help if there was some way of getting a writable stream to a folder/file that the user has selected from the filesystem

@jimmywarting
Copy link

Tada: https://github.com/jimmywarting/StreamSaver.js
...A more efficient way of saving large amount of data!

@annevk
Copy link
Member

annevk commented May 15, 2020

Let's merge this into #4148.

@annevk annevk closed this as completed May 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
addition/proposal New features or enhancements needs implementer interest Moving the issue forward requires implementers to express interest
Development

No branches or pull requests

4 participants