Skip to content

Instantly share code, notes, and snippets.

@qgustavor
Last active June 9, 2016 14:58
Show Gist options
  • Select an option

  • Save qgustavor/4a7222eb1867fe4c6751813176656630 to your computer and use it in GitHub Desktop.

Select an option

Save qgustavor/4a7222eb1867fe4c6751813176656630 to your computer and use it in GitHub Desktop.
Big File Download Test
<!doctype html>
<title>Big File Download Test</title>
<p>Please wait: a Service Worker will be installed then the download will be started.</p>
<p>You can also download a <a href="big-audio.wav">big random wav audio file</a> or listen it below:</p>
<p><audio src="big-audio.wav" preload="none" controls></p>
<p id="error-output"></p>
<script>
navigator.serviceWorker.register('sw.js', {scope: '.'})
.then(() => {
location.href = './big-file.txt';
}, err => {
document.getElementById('error-output')
.appendChild(document.createTextNode('Error: ' + err.toString()));
});
</script>
// From https://github.com/jakearchibald/isserviceworkerready/blob/master/src/demos/transform-stream/sw.js
self.addEventListener('install', event => {
self.skipWaiting();
});
self.addEventListener('activate', event => {
clients.claim();
});
// From https://jakearchibald.com/2016/streams-ftw/
self.addEventListener('fetch', event => {
var requestURL = new URL(event.request.url);
if (requestURL.origin != location.origin) return;
var textRequest = requestURL.pathname.endsWith('/big-file.txt');
var audioRequest = requestURL.pathname.endsWith('/big-audio.wav')
var validRequest = textRequest || audioRequest;
if (!validRequest) return;
// Default: 1 GB file, 1 MB chunks:
var fileSize = getParameterByName(requestURL.search, 'size') || Math.pow(1024, 3);
var chunkSize = getParameterByName(requestURL.search, 'chunk') || Math.pow(1024, 2);
var timeout;
var cancelled;
var header = audioRequest ? getAudioHeader(fileSize) : false;
var stream = new ReadableStream({
start(controller) {
// Our current position:
var pos = 0;
function push() {
if (cancelled) return;
// Are we done?
if (pos >= fileSize) {
controller.close();
return;
}
if (header) {
controller.enqueue(header);
pos += header.length;
header = false;
}
// Push data:
var randomData = generateRandomData(Math.min(chunkSize, fileSize - pos));
// Advance the position
pos += randomData.length;
controller.enqueue(randomData);
// push again as fast as possible
timeout = setTimeout(push, 0);
}
// Let's go!
push();
},
cancel() {
clearTimeout(timeout);
cancelled = true;
}
});
event.respondWith(new Response(stream, {
headers: {
'Content-Type': 'text/plain',
'Content-Disposition': 'attachment; filename="big-file.' + ( audioRequest? 'wav' : 'txt' ) + '"',
'Content-Length': fileSize
}
}));
});
function generateRandomData(l) {
var data = new Uint8Array(l);
do {
data[--l] = Math.random() * 256 | 0;
} while (l);
return data;
}
function getParameterByName(query, name) {
var match = RegExp('[?&]' + name + '=([^&]*)', 'g').exec(query);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
}
function pack(b,a){return[new Uint8Array([a,a>>8]),new Uint8Array([a,a>>8,a>>16,a>>24])][b]}
function getAudioHeader(size) {
return concatenate(Uint8Array, ["RIFF",pack(1,52),"WAVE","fmt ",pack(1,16),pack(0,1),pack(0,1),pack(1,44100),
pack(1,88200),pack(0,2),pack(0,16),"data",pack(1,16*size/8)]);
}
function concatenate(resultConstructor, arrays) {
let totalLength = 0;
for (let arr of arrays) {
totalLength += arr.length;
}
let result = new resultConstructor(totalLength);
let offset = 0;
for (let arr of arrays) {
if (typeof arr === 'string') {
arr = new Uint8Array(arr.split('').map(e => e.charCodeAt(0)));
}
result.set(arr, offset);
offset += arr.length;
}
return result;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment