Handling large file downloads with javascript

Viewed 2895

I have a server hosting very large files: > 50GB. Until now, these files were just downloaded via the browser normally. (<a href="...">)
The problem is, many times these are downloaded over slow/unreliable connections, and if the download fails, they have to start over from scratch.
As a solution to this, I want to make an alternative downloader in the browser, where each downloaded chunk is saved to some temporary storage with javascript, allowing the download to be resumed if it fails.

A good example of where something similar is being used, is mega.nz. Downloads are handled with javascript, and when the entire file is downloaded to a temporary location, the browser shows the classical file download dialog, which is actually referencing a blob url. I've come up with some solutions, but the difficult part is I need to support FF, Chrome, and IE 11+.

Solution #1) fetch + Filesystem API + HTTP Range header
I use fetch() and stream the result into a FileWriter. If at any time the download fails, I can check how many bytes were written into the file, and use the Range header on the next fetch(). When the download completes, I can get a url like filesystem:http://some-url/file, which I can assign as the a attribute of an href tag, with the download option set. I send a click() event, and the file is downloaded almost instantaneously. The entire file is not loaded into memory, so it can work with very large files, but this method only works on Chrome. I know there is a polyfill for the FileSystem API which uses indexedDB, but when I need to generate the url, it will give me a blob, which is loaded into memory. Also the writes load the entire value into memory, append to it, and then write it back. (Hardly efficient, and won't work with large files)

Solution #2) Service workers + IndexedDB
With a service worker, I can hijack the fetch event, and send back any data I want. I can use this to implement saving files to IndexedDB, and serving them from there chunk by chunk, without loading the entire thing into memory if I make a ReadableStream with the correct logic.

Solution #3) Flash (Actionscript)
Mega seems to use some Flash based downloader as a fallback for old browsers, haven't looked into it yet, but maybe this is a viable solution for IE.

Solution #4) ActiveX
Again a possible solution for IE. Can ActiveX be used to stream data straight to the filesystem without loading it in memory?

So far I'm missing support for IE, and for versions of FF where service workers are not enabled, like Firefox ESR. IE has IndexedDB, but in order to download the data, I have to make one blob which contains the entire file, and this gets loaded into memory

Is there anything else I'm missing? Any other way to implement this? Note: browser extensions are unfortunately off limits.

1 Answers

I'd recommend using the Background Fetch API, with the caveat that it's only supported in Chrome 74+ (and the corresponding Edge preview releases) at this time. It's designed for your use case.

I could imagine using that when supported, and falling back to an alternative on browsers that don't support it.

Related