应使用change事件监听,因其在用户确认选择文件后可靠触发;click无法捕获选中动作,input在Safari等浏览器中不可靠;需设multiple属性支持多文件,files为只读FileList,通过e.target.files[0]获取File对象。

如何监听 <input type="file"> 的文件选择事件
直接用 change 事件监听,别用 click 或 input —— 后者在部分浏览器(如 Safari)中无法可靠触发,click 则根本捕获不到选中动作。
关键点:用户点击“选择文件”并确认后,change 才会触发;取消选择或重复选同一个文件,某些浏览器可能不触发(需手动清空 input.files 或重置 input.value 来保证一致性)。
-
input.addEventListener('change', e => { const files = e.target.files; })——files是FileList,只读,类数组,不能用map等数组方法直接调用 - 若需多文件,务必设置
multiple属性:<input type="file" multiple>,否则files.length永远 ≤ 1 - 选中文件后,
e.target.files[0]是File对象,继承自Blob,有name、size、type、lastModified等属性
如何读取本地文件内容(文本/图片等)而不上传
用 FileReader,不是 fetch 或 XMLHttpRequest —— 那些是发请求用的,本地文件得靠 FileReader 异步读取。
常见错误:在 readAsText 后立刻访问 result,但此时读取未完成;必须等 load 事件。
const fr = new FileReader(); fr.onload = () => console.log(fr.result); fr.readAsText(file);- 读图片推荐用
readAsDataURL,结果是 base64 字符串,可直接赋给<img src> - 大文件慎用
readAsDataURL,内存占用高;超 50MB 建议改用readAsArrayBuffer+ 流式处理 -
FileReader不支持async/await,需封装 Promise,否则容易陷入回调地狱
如何通过 FormData 发起文件上传请求
后端接收的是 multipart/form-data,前端必须用 FormData 构造体,不能把文件塞进普通 JSON 对象里发过去 —— 那样后端收不到二进制数据。
注意:fetch 默认不设 Content-Type 头,交给浏览器自动加 boundary;手动设会导致失败。
-
const fd = new FormData(); fd.append('file', file); fd.append('filename', file.name);—— 键名要和后端约定一致(如file、upload、images[]) - 多个文件:循环
append,同名键名(如fd.append('files', f)多次)可让后端收到数组 - 用
fetch('/upload', { method: 'POST', body: fd })即可,不要加headers: {'Content-Type': '...'} - 上传进度需监听
XMLHttpRequest.upload.onprogress;fetch原生不支持进度,得用ReadableStream+pipeTo(兼容性差),实际项目中仍倾向XMLHttpRequest
常见报错与绕过限制的现实做法
Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob' —— 多半是传了字符串或 null 进去,检查 files[0] 是否存在,是否误用了 e.target.value(那是路径字符串,已废弃且不可靠)。
跨域、413 Payload Too Large、后端收不到字段,往往不是前端代码问题,而是配置漏项。
- Nginx 默认限制
client_max_body_size 1m,上传大文件必改配置 - Node.js 的
express需配express.json({ limit: '50mb' })和express.urlencoded({ limit: '50mb' }),但它们对multipart无效,得用multer或busboy - Chrome 对本地
file://协议禁用FileReader和表单提交,调试必须走http://localhost(用npx serve或 VS Code Live Server) - 移动端 iOS Safari 对
capture="camera"支持不稳定,建议 fallback 到默认文件选择器
真正麻烦的从来不是怎么写那几行 JS,而是边界情况:空文件、超时重试、断点续传、取消上传时 abort controller 的清理、以及后端返回的非标准错误格式——这些没统一处理,上线后第一周就会被 QA 打回来。


















