必须设置enctype="multipart/form-data",否则后端收不到文件;浏览器默认application/x-www-form-urlencoded无法传输二进制内容,且method必须为post,手动设置content-type会破坏boundary导致解析失败。

必须设置 enctype="multipart/form-data",否则后端收不到文件
这是最常踩的坑:表单没加这个属性,前端看似提交成功,后端却始终读不到文件。浏览器默认用 application/x-www-form-urlencoded 编码,它根本没法打包二进制内容。
正确写法只有这一种:<form action="/upload" method="post" enctype="multipart/form-data"></form>
- 缺了
enctype,input type="file"选了也白选 -
method必须是POST;GET不支持文件传输 - 后端框架(如 Express + multer、Django、Flask)都依赖这个 header 来启用 multipart 解析器
FormData.append() 的字段名要和后端约定一致
用 JavaScript 提交时,FormData.append('xxx', file) 里的 'xxx' 不是随便起的。后端解析时就靠这个名字取文件,对不上就返回空或 400 错误。
常见不匹配场景:
- Django 默认期待
request.FILES['file']→ 前端得写fd.append('file', file) - Express + multer 配了
upload.single('avatar')→ 前端必须用fd.append('avatar', file) - 如果后端接收多文件字段叫
documents[],前端 append 时名字也要带[](部分框架要求)
别手动设 Content-Type header —— FormData 发送时浏览器会自动生成带 boundary 的 multipart 头,你一覆盖,后端直接拒收。
用 input type="file" 获取文件,但不能直接赋值或模拟点击
安全机制决定了你无法用 JS 给 input 赋路径(el.value = 'xxx' 无效),也不能在非用户手势下调用 click()(Chrome/Firefox 会静默失败)。
可靠做法只有两种:
- 用
<label for="myFile">上传</label><input type="file" id="myFile">,点 label 自动触发选择框 - 用透明
input叠在按钮上:position: absolute; opacity: 0; top: 0; left: 0; width: 100%; height: 100%,确保点击区域完全覆盖
禁用 display: none 或 visibility: hidden —— 这会让某些浏览器判定为“不可交互”,click() 失效。
上传前校验大小和类型,但仅作提示,后端必须重验
前端校验是为了快速反馈,不是安全防线。用户改 JS 就能绕过,所以后端校验不可省略。
基础校验示例:
const file = input.files[0];
if (file.size > 5 * 1024 * 1024) {
alert('文件不能超过 5MB');
input.value = ''; // 清空选择状态
return;
}
if (!file.type.startsWith('image/')) {
alert('仅支持图片文件');
return;
}
注意:file.type 来自文件扩展名或系统 MIME 推断,不可信;真正可靠的类型判断得靠后端读文件头(如 file-type 库)。
更关键的是:所有限制(大小、类型、数量、文件名格式)必须在后端再次执行,否则可能引发存储爆炸、任意文件执行等风险。
上传逻辑本身不难,难的是边界处理——比如大文件卡住、网络中断、重复提交、恶意文件名(../../../etc/passwd)、并发上传冲突。这些不在基础表单里体现,但上线前必须补全。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











