@quyse/client-zip
Version:
A tiny and fast client-side streaming ZIP generator
78 lines (71 loc) • 3.67 kB
HTML
<html>
<head>
<meta charset="UTF-8">
<title>ServiceWorker streaming demo</title>
<style>
html { height: 100%; font-size: 16px; font-family: sans-serif; }
body { min-height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; }
p, form { width: 80vw; margin-top: 0; }
h1 { text-align: center; font-size: 1.3rem; margin-bottom: 2rem; }
h2 { font-size: 1rem; }
button, input { font-size: inherit; }
label { display: inline-flex; width: calc(100% - 3em); align-items: start; height: 1.5em; }
input { color: blue; border: 0 none; padding: 0; margin: 0; border-bottom: 1px solid blue; flex-grow: 1; }
input:invalid { color: black; border-color: gray; }
button { line-height: 1.4em; border: 0 none; background-color: #b4c6d8; border-radius: 0.2em; padding: 0.3em 0.5em; transition: background-color 0.3s; }
button:hover, button:active { background-color: #a6b7c9; }
button[type=submit] { background-color: #84c6f1; }
button[type=submit]:hover, button[type=submit]:active { background-color: #75b4df; }
form:invalid button[type=submit] { background-color: #dbdbdb; color: gray; }
</style>
</head>
<body>
<h1>ServiceWorker streaming demo</h1>
<p>This shows how you can leverage a ServiceWorker to <strong>stream</strong> without ever having to store
a full Blob of the ZIP in the browser.
</p>
<p>I'm doing it here by POSTing a simple form with an arbitrary number of URL fields
(therefore no JavaScript is needed to send the list — no <em>onsubmit</em> — but only to register the ServiceWorker
and clone the URL input). You could also use <em>hidden</em> fields to specify some extra URLs or headers,
even specify all the URLs that way to make a non-interactive form with only a submit button visible.
</p>
<p>The ServiceWorker (just 14 lines) intercepts the request, lazily fetches the URLs found in the form data
(all the fields named "url") with a <a href="https://github.com/Touffy/dl-stream">DownloadStream</a>,
feeds that to <code>downloadZip</code> and responds with <code>downloadZip</code>'s return value (which is a Response).
That's it.
The process is only a bit more complicated that using a Blob (<a href="worker.js">look at the code</a>).
</p>
<form name="download" action="downloadZip/demo.zip" method="POST">
<h2>URLs of documents to include in the ZIP (for cross-origin URLs, the server must send CORS headers):</h2>
<ol id="file-list">
<li><label><span>URL: </span><input type="url" name="url" required></label></li>
</ol>
<button name="more" type="button">➕ Add a URL</button>
<button type="reset">🧽 Clear the URLs</button>
<button type="submit">⤓ Download all that in one ZIP</button>
</form>
<template id="url-input">
<li>
<label><span>URL: </span><input type="url" name="url" required></label>
<button type="button" title="remove this link">❌</button>
</li>
</template>
<script async>
// the important part: register the ServiceWorker !
navigator.serviceWorker.register('./worker.js')
// this part implements a very simple dynamic form where you can clone and remove the basic input
const list = document.getElementById("file-list")
const template = document.getElementById("url-input")
document.forms.download.more.onclick = () => {
list.appendChild(template.content.cloneNode(true))
}
document.forms.download.onreset = () => {
while (list.childElementCount > 1) list.lastElementChild.remove()
}
list.addEventListener("click", (event) => {
if (event.target.title === "remove this link") event.target.parentElement.remove()
})
</script>
</body>
</html>