SvelteKit大文件上传报错:Failed to parse body as FormData
问题
我正在用SvelteKit开发Google相册导出文件(含图片的大型Zip文件)上传功能,前端代码如下:
console.log(`uploading google archive file ${i}/${files.length}`); const file = files[i]; const xhr = new XMLHttpRequest(); xhr.open('POST', '/api/google-import'); xhr.onload = () => { if (xhr.status >= 200 && xhr.status < 300) { resolve(); } else { reject(new Error(`Archive upload failed with status ${xhr.status}.`)); } }; xhr.onerror = () => { reject(new Error('Archive upload failed due to a network error.')); }; const formData = new FormData(); formData.append('file', file); xhr.send(formData);
小型JPG上传正常,但上传10GB级别的Zip文件时触发500错误,后端简化代码如下:
export const POST: RequestHandler = async ({ request }) => { const formData = await request.formData(); return new Response('', { status: 200 }); };
报错信息:
[500] POST /api/google-import TypeError: Failed to parse body as FormData. at parsingError (node:internal/deps/undici/undici:6114:14)
已设置环境变量BODY_SIZE_LIMIT为60G,但问题仍未解决,请问遗漏了什么配置?
解决方案
1. 改用流式处理替代内存加载
直接调用request.formData()会将整个大文件加载到内存,这是解析失败的核心原因。改用流式处理,借助busboy库分块读取文件:
import type { RequestHandler } from '@sveltejs/kit'; import busboy from 'busboy'; export const POST: RequestHandler = async ({ request }) => { const contentType = request.headers.get('content-type'); if (!contentType?.includes('multipart/form-data')) { return new Response('Invalid content type', { status: 400 }); } const bb = busboy({ headers: Object.fromEntries(request.headers.entries()) }); return new Response(async () => { return new ReadableStream({ start(controller) { bb.on('file', (_name, file) => { file.on('data', chunk => controller.enqueue(chunk)); file.on('end', () => controller.close()); file.on('error', err => controller.error(err)); }); bb.on('error', err => controller.error(err)); // 将请求体流导入busboy request.body?.pipeTo(new WritableStream({ write(chunk) { bb.write(chunk); }, close() { bb.end(); } })).catch(err => controller.error(err)); } }); }, { status: 200 }); };
这种方式不会将整个文件存入内存,从根源避免解析失败。
2. 检查Undici的深层配置
SvelteKit依赖Undici处理HTTP请求,除了BODY_SIZE_LIMIT,还需确保Undici的maxBodySize和超时配置足够。可以在hooks.server.ts中针对特定请求调整:
import type { Handle } from '@sveltejs/kit'; export const handle: Handle = async ({ event, resolve }) => { if (event.request.url.includes('/api/google-import') && event.request.method === 'POST') { // 重写formData方法,避免内置解析的内存限制 const originalFormData = event.request.formData; event.request.formData = async () => { // 这里可以用第三方库手动解析multipart流,或者直接处理原始流 const body = await event.request.arrayBuffer(); // 示例:返回空FormData(实际需根据业务处理) return new FormData(); }; } return resolve(event); };
3. 排查部署平台限制
如果部署在Vercel、Netlify等平台,平台自身可能有请求体大小限制:
- Vercel Serverless函数默认最大请求体4.5GB,需改用Edge函数或配置Blob存储中转
- Netlify函数默认限制10MB,需调整为大文件上传方案(如直接上传到对象存储)
如果是自部署Node.js服务器,需检查适配器的配置,比如使用@sveltejs/adapter-node时,在server.js中添加:
import express from 'express'; import { handler } from './build/handler.js'; import multer from 'multer'; const app = express(); // 配置multer处理大文件上传 const upload = multer({ dest: 'uploads/', limits: { fileSize: 60 * 1024 * 1024 * 1024 } // 60GB限制 }); // 优先用multer处理上传请求 app.post('/api/google-import', upload.single('file'), (req, res) => { // 处理上传后的文件 res.sendStatus(200); }); app.use(handler); app.listen(3000);
4. 前端优化上传配置
大文件上传易超时,前端需添加超时处理和进度监听:
xhr.timeout = 3600000; // 设置1小时超时 xhr.ontimeout = () => reject(new Error('Upload timed out')); // 添加上传进度监听 xhr.upload.onprogress = (e) => { if (e.lengthComputable) { const progress = (e.loaded / e.total) * 100; console.log(`Upload progress: ${progress.toFixed(2)}%`); } };
内容的提问来源于stack exchange,提问作者munHunger
相关产品推荐
相关产品推荐

