Remix.run文件上传问题:Action中File对象变为字符串
Remix表单上传文件时Action中无法识别File对象的问题
我正在开发一个Remix应用,允许用户通过表单上传文件。前端编写了handleSubmit函数处理表单提交,将包含文件的FormData发送到Action,但在Action中接收时,文件变成了字符串,用value instanceof File检查返回false。
前端handleSubmit代码
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); const requiredFields = [ "projectName", "blockchain", "supply", "mintPrice", "mintDate", "twitterLink", "emailContact", "description", "teamSummary", "teamMembers", "references", "documents", "artwork", "marketing", "allowlist", "investment", ]; const newErrors = {...initialErrors}; let $form = event.currentTarget; let formData = new FormData($form); requiredFields.forEach(field => { if (!projectForm[field as keyof ProjectFormState]) { newErrors[field] = true; } }); if (Object.values(newErrors).some(value => value)) { setErrors(newErrors); return; } console.log('projectForm', projectForm); Object.entries(projectForm).forEach(([key, value]) => { console.log('file type in submit', value instanceof File) if (value instanceof File) { console.log('file value', value) formData.append(key, value, value.name); } else { formData.set(key, value); } }); submit(formData, { method: $form.getAttribute("method") as HTMLFormMethod ?? $form.method, action: $form.getAttribute("action") ?? $form.action, }); };
前端日志显示文件是正常的File对象,但在Action中出现问题:
Action代码
export const action = async ({request}: ActionArgs) => { const formData = await request.formData(); const project: ProjectFormFields = {} as ProjectFormFields; await Promise.all(Array.from(formData.entries()).map(async ([key, value]) => { console.log(key, value); // Log the value of 'value' console.log(value instanceof File); // Check whether 'value' is a File instance if (value instanceof File) { const arrayBuffer = await value.arrayBuffer(); project[key] = Buffer.from(arrayBuffer); } else { project[key] = value; } })); console.log(project) try { const response = await putProjectForm(project); const {status} = response; console.log('Form submission response', status); return redirect("/", { headers: { "Set-Cookie": `message=Your form was submitted successfully; Path=/;`, }, }); } catch(error) { console.error('There was an error with the form submission', error); } return null;
console.log(value instanceof File)返回false,无法正确识别文件。
解决方法
1. 服务器端检查Blob而非File
在Remix的Action(Node.js环境)中,上传的文件对象是Blob实例,而不是浏览器端的File对象(File是Blob的子类,但Node.js环境下的FormData实现没有File,只有Blob)。因此需要修改检查逻辑:
// 替换原来的instanceof File检查 if (value instanceof Blob) { const arrayBuffer = await value.arrayBuffer(); project[key] = Buffer.from(arrayBuffer); } else { project[key] = value; }
如果需要更严谨的判断,可以同时检查是否包含name属性(模拟File的行为):
if (value instanceof Blob && 'name' in value) { // 处理文件 }
2. 优化前端FormData生成逻辑
你当前先通过new FormData($form)获取表单原生数据,再用projectForm的字段覆盖,这可能导致重复或错误覆盖文件:
- 如果表单中的文件输入已经被FormData捕获,再次用
formData.append/set可能导致字段重复或替换错误 - 确保projectForm中的文件确实是File对象(受控组件中正确处理文件上传的
onChange事件,比如将e.target.files[0]存入状态)
建议直接使用表单原生的FormData,而非手动覆盖:
// 移除手动遍历projectForm的代码,直接使用原生FormData let formData = new FormData($form); // 仅在需要补充额外字段时再append,不要覆盖已有表单字段
如果必须使用projectForm的状态来生成FormData,确保所有文件字段都是正确的File对象,并且不要重复处理相同字段。
内容的提问来源于stack exchange,提问作者Oliver Darby
相关产品推荐
相关产品推荐

