Remix上传大体积视频至S3报MaxPartSizeExceededError解决方案
问题背景
在Remix框架下实现视频上传功能,参考官方示例完成基础开发后,小体积图片可正常上传,但上传大体积视频时触发如下报错:
MaxPartSizeExceededError: Field "file" exceeded upload size of 3000000 bytes.
当前实现的表单与Action代码如下:
export const action: ActionFunction = async ({ request, params }) => { const uploadHandler: UploadHandler = composeUploadHandlers( s3UploadHandler, createMemoryUploadHandler() ); const formData = await parseMultipartFormData(request, uploadHandler); const newDesc = formData.get("title"); const newFile = formData.get("file"); return json({ newDesc, newFile, }); } const Create = () => { const [asset, setAsset] = useState<File | null>(null); const [title, setTitle] = useState(''); const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { setTitle(event.target.value); }; const onChangeAsset = async (evt) => { const file = evt?.target?.files?.[0] if (!file) return setAsset(null) setAsset(file) } return ( <Box> <Form method="post" action='/create' encType="multipart/form-data"> {/* 文件上传组件 */} <Upload sublabel='Add video to upload to S3' name='asset' onChange={onChangeAsset} value={asset} maxSize={52000000000} /> {/* 文本输入组件 */} <TextField name='title' label="Title" value={title} onChange={handleChange} /> <Button type='submit'>Submit</Button> </Form> </Box> ) } export default Create
查阅官方文档可见createMemoryUploadHandler的说明如下:
配置项: 该API仅支持
maxPartSize和filter两个配置项,用法与unstable_createFileUploadHandler一致,不推荐在大规模生产场景使用,仅适合简单场景下的便捷调用。
解决方案
报错核心原因是createMemoryUploadHandler默认单分片大小限制为3MB,即报错提示的3000000字节,且该处理器将所有上传内容存在服务端内存中,本身就不适合处理大体积文件,否则极易触发服务端内存溢出。可选实现方案如下:
方案1:使用本地临时文件上传处理器(适合百兆内视频、部署环境有可写磁盘的场景)
弃用默认配置的内存处理器,换成Remix内置的文件上传处理器,将上传分片先写入服务端本地临时目录,避免占用内存,同时手动配置匹配业务需求的大小限制:
- 首先修复代码里的字段名不匹配问题:当前上传组件的
name属性值为asset,但Action中通过formData.get("file")取值,二者对齐才能正常拿到文件对象 - 引入
unstable_createFileUploadHandler替换内存处理器,配置示例:
import { unstable_createFileUploadHandler as createFileUploadHandler } from "@remix-run/node"; export const action: ActionFunction = async ({ request, params }) => { const uploadHandler: UploadHandler = composeUploadHandlers( s3UploadHandler, createFileUploadHandler({ maxPartSize: 52000000000, // 和前端上传组件的maxSize保持一致,单位为字节 directory: "./tmp/uploads", // 临时文件存储目录,需确保部署环境有写入权限 filter: ({ name }) => name !== "asset" // 非文件字段走内存处理,文件字段走自定义s3UploadHandler }) ); const formData = await parseMultipartFormData(request, uploadHandler); const newDesc = formData.get("title"); const newFile = formData.get("asset"); // 字段名和前端name对齐 return json({ newDesc, newFile, }); }
注意:该方案不支持只读磁盘的Serverless部署环境,大文件上传时长时间占用服务端连接,超过500M的视频不推荐用这种中转上传模式
方案2:S3预签名直传(生产环境大体积视频推荐方案)
完全绕开Remix服务端的上传中转逻辑,让客户端直接将文件上传到S3,从根源避免服务端解析大小限制、内存/磁盘占用、连接超时问题,实现流程:
- 新增一个专门生成S3预签名上传URL的Action,预签名规则中可自定义允许上传的最大文件体积、文件类型、URL过期时间
- 前端提交表单时,先请求该接口拿到预签名URL,将选中的视频文件直接通过PUT请求上传到S3,拿到文件最终的S3访问地址
- 前端再将视频的S3地址、标题等其他表单字段提交给业务Action,完成数据落库等后续逻辑
- 若需要支持几十G的超大文件、断点续传能力,可搭配S3的分片上传API实现前端分片上传,上传稳定性和速度远高于服务端中转模式
常见踩坑点
- 如果服务前挂了Nginx、CDN等反向代理,需要同步调整代理层的最大请求体限制,否则请求会在到达Remix服务前被代理拦截
- 即便调大
maxPartSize参数,也不要用createMemoryUploadHandler处理超过100M的文件,否则极易触发服务端OOM崩溃 - 用预签名直传方案时,一定要在预签名规则里严格限制文件类型、大小,避免被恶意利用上传违规文件到S3存储桶
内容的提问来源于stack exchange,提问作者Rin3
相关产品推荐
相关产品推荐

