Event to indicate download started and/or finished
sicking opened this issue · 9 comments
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 clickThe 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.
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.
I thought that cloning happened once loading starts? Is that different for <a>? Or for a larger set of features? Where is this defined?
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.
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?
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?
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.
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
Tada: https://github.com/jimmywarting/StreamSaver.js
...A more efficient way of saving large amount of data!