enctype="multipart/form-data" 是文件上传的强制要求,必须显式设置在 form 标签上且 method 为 post;其他编码格式无法传输二进制文件,accept 仅为前端提示,后端须校验 mime 类型、魔数及文件大小等。

enctype="multipart/form-data" 是文件上传的强制要求,不是可选项
只要表单里有 <input type="file">,就必须在 <form></form> 标签上显式写 enctype="multipart/form-data",且 method 必须为 POST。浏览器不会自动补全,也不接受默认值——设错或漏设,后端根本收不到文件对象(比如 req.file 或 request.files 为空)。
常见错误现象:
- 前端选了文件、点了提交,但后端日志里没打印出任何文件字段
- 用
application/json提交,后端收到的是[object File]字符串 - 用
application/x-www-form-urlencoded,文件内容被 URL 编码成乱码,原始字节丢失
原因很简单:multipart/form-data 是唯一能携带原始二进制分块(含 Content-Disposition 和边界分隔符)的编码格式;其他类型压根不支持二进制边界结构。
form 标签上写 enctype,不是 input 或 button 上
enctype 属于 <form></form> 的属性,只对整个表单生效。它不会因为某个 input 是 type="file" 就自动触发,也不会继承自父级或通过 JS 动态添加后“追加生效”——必须在 HTML 渲染时就存在。
实操建议:
- 检查源码,确认
<form action="/upload" method="post" enctype="multipart/form-data"></form>这一行完整存在 - 不要写成
<input enctype="...">,这毫无意义 - 如果用 JavaScript 动态创建表单,记得在
appendChild前给form元素设置enctype属性:form.enctype = "multipart/form-data" - 使用
fetch+FormData时不用手动设enctype,浏览器会自动加请求头Content-Type: multipart/form-data; boundary=...
accept 属性只是客户端提示,不能替代后端校验
<input type="file" accept="image/png,image/jpeg"> 能在部分浏览器中过滤文件类型选择弹窗,但它完全可被绕过:用户可以手动修改 HTML、用 curl 提交、或拖拽非允许类型文件。所以它只影响体验,不提供安全保证。
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
真正起作用的只有后端:
- 检查
Content-Type头(如image/jpeg),而非仅靠扩展名 - 读取文件魔数(magic bytes),比如 PNG 文件开头是
89 50 4E 47 - 限制总请求体大小(如 Node.js 的
multer.limits.fileSize,Flask 的MAX_CONTENT_LENGTH) - 重命名文件(避免
../../etc/passwd类路径遍历),用secure_filename()或等效函数处理原始名
不校验 MIME 或魔数,单靠 accept 或后缀名白名单,很容易被传入伪装的 WebShell 文件。
FormData 对象内部已隐式使用 multipart/form-data
当用 JavaScript 构造 FormData 并 append 文件时,例如:
const fd = new FormData();
fd.append("avatar", fileInput.files[0]);
fetch("/upload", { method: "POST", body: fd });
浏览器会自动以 multipart/form-data 编码发送,且生成合法的 boundary。你不需要、也不能手动设置 Content-Type 请求头——一旦显式设置了,浏览器反而会禁用自动 multipart 封装,导致服务端解析失败。
容易踩的坑:
- 写了
headers: { "Content-Type": "multipart/form-data" }→ 错误,会丢掉 boundary,后端收不到文件 - 把
fileInput.files[0]转成 blob 再 append → 没问题,FormData仍能正确封装 - append 普通字符串字段(如
fd.append("name", "Alice"))→ 它会和文件一起打包进同一个 multipart 请求,服务端可同时拿到文本和二进制
边界(boundary)是随机生成的,每次请求都不同,服务端解析器(如 multer、Django 的 request.FILES)依赖它来切分各个 part —— 这个细节前端不用管,但得知道它真实存在且不可省略。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










