perf(browse): stream files into the offline zip instead of buffering all bytes
downloadFsSubtree pre-read every file's arrayBuffer() and handed the raw ArrayBuffer to JSZip, so the entire subtree's bytes sat in the JS heap at once before zipping — the likely OOM on a large local folder despite the size warning. Hand JSZip the File (a Blob backed by disk) instead; it reads each lazily during generateAsync, dropping peak memory to roughly the zip output plus JSZip's working set. Also document, on downloadUrl, why server-side download errors aren't surfaced as toasts: the <a download> click is fire-and-forget, and the folder path targets zddc-server's streamed virtual "<dir>.zip" endpoint — routing it through fetch() to make errors catchable would defeat the streaming for arbitrarily large archives. Left as a known, documented limitation rather than a buffering regression. All 6 browse Playwright specs pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b2c16063c4
commit
d524966f00
1 changed files with 11 additions and 2 deletions
|
|
@ -44,6 +44,12 @@
|
|||
}
|
||||
|
||||
// Trigger a download from a same-origin server URL via Content-Disposition.
|
||||
// NOTE: an <a download> click is fire-and-forget — a server error
|
||||
// (401/403/404/5xx) can't be observed here, so failures surface only as
|
||||
// the browser's own download error, not a toast. This is deliberate: the
|
||||
// folder path points at zddc-server's streamed virtual "<dir>.zip"
|
||||
// endpoint, and buffering it through fetch() to make errors catchable
|
||||
// would defeat the streaming (the archive can be arbitrarily large).
|
||||
function downloadUrl(filename, url) {
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
|
|
@ -97,9 +103,12 @@
|
|||
var zip = new window.JSZip();
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
ev.statusInfo('Zipping ' + rootHandle.name + '… (' + (i + 1) + '/' + files.length + ')');
|
||||
// Hand JSZip the File (a Blob, backed by disk) rather than
|
||||
// pre-reading every file's arrayBuffer — otherwise the whole
|
||||
// tree's raw bytes sit in the JS heap at once before zipping.
|
||||
// JSZip reads each Blob lazily during generateAsync.
|
||||
var f = await files[i].handle.getFile();
|
||||
var buf = await f.arrayBuffer();
|
||||
zip.file(rootHandle.name + '/' + files[i].relPath, buf);
|
||||
zip.file(rootHandle.name + '/' + files[i].relPath, f);
|
||||
}
|
||||
ev.statusInfo('Generating ' + rootHandle.name + '.zip…');
|
||||
var blob = await zip.generateAsync({ type: 'blob' });
|
||||
|
|
|
|||
Loading…
Reference in a new issue