Send stored audio as a Blob body so large files upload on HTTP/1.1
Files over 8 MB were sent as a ReadableStream with duplex: 'half'. Chrome only allows streaming request bodies over HTTP/2+, so every such upload failed with "Failed to fetch" on HTTP/1.1 (e.g. http://localhost). A Blob body is read lazily from OPFS by the browser, so it keeps the "never hold the whole master in memory" property without depending on the protocol. Verified in headless Chromium: a 33 MB WAV drops and analyses both at http://localhost:8050 and behind Cloudflare/Caddy at a /uj/ prefix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015UXA39Sh9coAx4FhRkd8R2
This commit is contained in:
+12
-30
@@ -165,41 +165,23 @@ export async function sweepIncomplete() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Stream stored audio to an endpoint as a raw body.
|
||||
* Send stored audio to an endpoint as a raw body.
|
||||
*
|
||||
* The body is sent as a stream (duplex: 'half') so a 200 MB file doesn't have to
|
||||
* be materialised as a second in-memory copy just to be uploaded.
|
||||
* The OPFS File is passed to fetch as-is: the browser reads a Blob body lazily
|
||||
* from its backing store, so a 200 MB master is never materialised in memory.
|
||||
* (A ReadableStream body with duplex: 'half' was used before, but Chrome only
|
||||
* allows streaming uploads over HTTP/2+, so any file past the old 8 MB cutoff
|
||||
* failed with "Failed to fetch" on HTTP/1.1 — e.g. http://localhost.)
|
||||
*/
|
||||
export async function send(fileId, url, {method = 'POST', onProgress} = {}) {
|
||||
const blob = await get(fileId);
|
||||
if (!blob) throw new Error('audio is no longer in browser storage — re-add the file');
|
||||
|
||||
// Small files go as a plain body: some proxies mishandle streaming duplex
|
||||
// requests, and below a few MB there is nothing to gain.
|
||||
if (blob.size < 8 * 1024 * 1024) {
|
||||
const buf = await blob.arrayBuffer();
|
||||
const res = await fetch(url, {
|
||||
method,
|
||||
headers: {'Content-Type': 'application/octet-stream'},
|
||||
body: blob,
|
||||
});
|
||||
if (onProgress) onProgress(blob.size, blob.size);
|
||||
return fetch(url, {
|
||||
method,
|
||||
headers: {'Content-Type': 'application/octet-stream'},
|
||||
body: buf,
|
||||
});
|
||||
}
|
||||
|
||||
let sent = 0;
|
||||
const body = blob.stream().pipeThrough(
|
||||
new TransformStream({
|
||||
transform(chunk, controller) {
|
||||
sent += chunk.byteLength;
|
||||
if (onProgress) onProgress(sent, blob.size);
|
||||
controller.enqueue(chunk);
|
||||
},
|
||||
}),
|
||||
);
|
||||
return fetch(url, {
|
||||
method,
|
||||
headers: {'Content-Type': 'application/octet-stream'},
|
||||
body,
|
||||
duplex: 'half',
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user