<input type="file"> 仅触发选择,不自动上传;需配合 JS 或表单提交,必设 name,多选需 multiple,accept 仅提示类型,上传须用 FormData 配合 fetch,禁手动设 Content-Type。

HTML 中 <input type="file"> 的基础写法和常见陷阱
直接用 <input type="file"> 只能触发选择,不会自动上传——这是新手最常误以为“点了就传了”的地方。它本身只是个文件选择控件,选完后必须配合 JavaScript 或表单提交才能把文件发出去。
- 必须加
name属性,否则 FormData 里拿不到字段名(服务端收不到) - 如需多选,得显式加
multiple,仅靠 Ctrl/Shift 点击不生效 - 默认只允许用户选择本地文件,无法通过 JS 赋值
input.value(安全限制,设了也无效) - 若要限制类型,用
accept="image/*,.pdf",但只是浏览器提示,不校验真实 MIME 类型
用 fetch + FormData 上传单个文件(推荐现代写法)
绕过表单提交、更可控、支持进度监听。关键点在于:不能把 File 对象直接 JSON.stringify,必须塞进 FormData;且服务端要能解析 multipart/form-data。
const input = document.querySelector('input[type="file"]');
input.addEventListener('change', async (e) => {
const file = e.target.files[0];
if (!file) return;
<p>const formData = new FormData();
formData.append('upload', file); // 'upload' 是字段名,需和服务端约定</p><p>try {
const res = await fetch('/api/upload', {
method: 'POST',
body: formData // 不要设 Content-Type,浏览器会自动加 boundary
});
console.log(await res.json());
} catch (err) {
console.error(err);
}
});
-
formData.append()第二个参数必须是File或Blob,传字符串会当普通文本传 - 不要手动设置
headers: { 'Content-Type': 'multipart/form-data' },这会让请求失败——浏览器必须自己生成 boundary - 如需传额外参数(如 user_id),用
formData.append('user_id', '123')即可,服务端一样能收到
处理多文件、大文件或拖拽上传的现实问题
用户一次选 10 个文件,或上传 500MB 视频时,input.files 虽然能拿到列表,但直接全塞进 FormData 可能卡死或超内存。得拆解、限并发、加校验。
- 检查
file.size,前端拦截超限文件(比如 >100MB),避免白传:if (file.size > 100 * 1024 * 1024) alert('文件太大'); - 拖拽上传需监听
drop和dragover,并调用e.preventDefault(),否则浏览器会打开文件 - 上传多个文件时,别用
for...of串行等每个完成——用Promise.allSettled()并发控制数量(例如最多 3 个并发) - 大文件建议分片(
slice()),但那是后端配合的方案,纯前端用input无法绕过浏览器一次性读入内存的限制
后端接收不到文件?重点排查这三处
90% 的“前端传了,后端收不到”问题出在协议或配置层,不是代码逻辑错。
- Node.js Express 用户:必须装
multer中间件,原生req.body解析不了 multipart - Nginx 默认限制上传大小(
client_max_body_size 1m),上传失败时浏览器可能静默卡住,查 Nginx error log 最准 - Spring Boot 用户:确认
spring.servlet.multipart.max-file-size和max-request-size都调高了,且是application.properties,不是yaml里拼错层级
真正难的从来不是怎么点开文件选择框,而是上传中途断连怎么续、校验失败怎么精准提示、大文件如何不崩浏览器——这些都得在 input[type="file"] 之后一层层补全。


















