Stream zip archive in download-directory API

Refactored the zip archive creation to stream output directly from the zip process, improving memory efficiency and response time. This fixes out-of-memory crashes on low-end devices.
This commit is contained in:
Tilman 2025-12-03 00:25:50 +01:00
parent 8d78e1f25c
commit c3609984b5

View file

@ -39,25 +39,55 @@ export const handler: Handlers<Data, FreshContextState> = {
const userRootPath = join(filesRootPath, context.state.user.id);
const fullDirectoryPath = join(userRootPath, directoryPath);
// Use the zip command to create the archive
// Use the zip command to create the archive with streaming
const zipProcess = new Deno.Command('zip', {
args: ['-r', '-', '.'],
cwd: fullDirectoryPath,
stdout: 'piped',
stderr: 'piped',
}).spawn();
// Get the zip stream from the process stdout
const zipStream = zipProcess.stdout;
// Monitor process errors and log them (stream will end on error)
zipProcess.status.then(async (status) => {
if (status.code !== 0) {
console.error('Zip command failed with code:', status.code);
// Read and log stderr for error details
try {
const stderrReader = zipProcess.stderr.getReader();
try {
const chunks: Uint8Array[] = [];
while (true) {
const { done, value } = await stderrReader.read();
if (done) break;
if (value) chunks.push(value);
}
if (chunks.length > 0) {
// Concatenate chunks
const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
const combined = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of chunks) {
combined.set(chunk, offset);
offset += chunk.length;
}
const errorText = new TextDecoder().decode(combined);
console.error('Zip stderr:', errorText);
}
} finally {
stderrReader.releaseLock();
}
} catch (error) {
console.error('Error reading stderr:', error);
}
}
}).catch((error) => {
console.error('Zip process error:', error);
});
const { code, stdout, stderr } = await zipProcess.output();
if (code !== 0) {
const errorText = new TextDecoder().decode(stderr);
console.error('Zip command failed:', errorText);
return new Response('Error creating zip archive', { status: 500 });
}
return new Response(stdout, {
return new Response(zipStream, {
status: 200,
headers: {
'content-type': 'application/zip',