Implement streaming file download for backup route
- Enhanced the backup server route to support streaming of large backup files using Node.js streams. - Improved error handling for file access and added appropriate HTTP responses for missing parameters and inaccessible files. - Updated response headers to include content length and type for better client handling.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,12 @@
|
|||||||
import { env } from '$env/dynamic/private';
|
import { env } from '$env/dynamic/private';
|
||||||
|
import { createReadStream } from 'node:fs';
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
import { Readable } from 'node:stream';
|
||||||
|
import type { RequestHandler } from './$types';
|
||||||
|
|
||||||
// GET /backup?file=nombre.bak -> descarga el archivo físico desde BACKUP_PATH
|
// GET /backup?file=nombre.bak -> descarga el archivo físico desde BACKUP_PATH (streaming; soporta archivos muy grandes)
|
||||||
export const GET = async ({ url }: { url: URL }) => {
|
export const GET: RequestHandler = async ({ url, request }) => {
|
||||||
const fileName = url.searchParams.get('file');
|
const fileName = url.searchParams.get('file');
|
||||||
if (!fileName) {
|
if (!fileName) {
|
||||||
return new Response('Missing file parameter', { status: 400 });
|
return new Response('Missing file parameter', { status: 400 });
|
||||||
@@ -28,14 +31,20 @@ export const GET = async ({ url }: { url: URL }) => {
|
|||||||
console.error('Backup path is not a regular file:', filePath);
|
console.error('Backup path is not a regular file:', filePath);
|
||||||
return new Response('Backup file not found or inaccessible', { status: 404 });
|
return new Response('Backup file not found or inaccessible', { status: 404 });
|
||||||
}
|
}
|
||||||
console.log('Serving backup file from', filePath);
|
console.log('Serving backup file from', filePath, 'size', st.size);
|
||||||
const data = await fs.readFile(filePath);
|
const nodeStream = createReadStream(filePath, { highWaterMark: 1024 * 1024 });
|
||||||
|
request.signal.addEventListener('abort', () => {
|
||||||
|
nodeStream.destroy();
|
||||||
|
});
|
||||||
|
const body = Readable.toWeb(nodeStream) as unknown as ReadableStream<Uint8Array>;
|
||||||
const headers = new Headers();
|
const headers = new Headers();
|
||||||
headers.set('Content-Type', 'application/octet-stream');
|
headers.set('Content-Type', 'application/octet-stream');
|
||||||
headers.set('Content-Disposition', `attachment; filename="${fileName}"`);
|
headers.set('Content-Disposition', `attachment; filename="${fileName}"`);
|
||||||
return new Response(data, { status: 200, headers });
|
headers.set('Content-Length', String(st.size));
|
||||||
} catch (e: any) {
|
return new Response(body, { status: 200, headers });
|
||||||
console.error('Error serving backup file:', e?.message ?? e);
|
} catch (e: unknown) {
|
||||||
|
const msg = e instanceof Error ? e.message : String(e);
|
||||||
|
console.error('Error serving backup file:', msg);
|
||||||
return new Response('Backup file not found or inaccessible', { status: 404 });
|
return new Response('Backup file not found or inaccessible', { status: 404 });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user