JavaScript无法直接读写本地文件,需通过获取用户选择的File对象,用FormData封装后通过fetch或XMLHttpRequest上传;注意兼容性、服务端配置及错误处理。

JavaScript 本身不能直接读写本地文件系统,但可以通过浏览器提供的标准 API 实现用户主动选择并上传文件。核心是 File、FileList、FormData 和 XMLHttpRequest 或 fetch 的组合使用。
如何用 获取用户选中的文件
这是最常见、最可控的入口。用户点击后触发 change 事件,从 event.target.files 中拿到 FileList 对象。
注意:files 是只读的类数组对象,不是真正的 Array,不能直接用 map 等方法 —— 需转成数组再操作。
-
multiple属性决定是否允许多选() -
accept属性可限制类型(如accept="image/*,.pdf"),但只是提示,不阻止用户绕过 -
files[0]是第一个File实例,它继承自Blob,有name、size、type、lastModified等属性
const input = document.querySelector('input[type="file"]');
input.addEventListener('change', (e) => {
const files = Array.from(e.target.files); // 转为数组便于处理
files.forEach(file => {
console.log(file.name, file.size, file.type);
});
});如何用 FormData 构建上传数据
FormData 是专为表单提交设计的接口,能自动处理文件字段的 Content-Type(包括 boundary),比手动拼接更可靠。它支持追加字符串或 Blob/File。
立即学习“Java免费学习笔记(深入)”;
- 上传单个文件:
formData.append('file', file) - 上传多个同名字段:
formData.append('files', file1)、formData.append('files', file2) - 上传时附带其他参数:
formData.append('userId', '123') - 不要手动设置
Content-Type头 ——fetch或XHR发送FormData时会自动设置为multipart/form-data并生成正确 boundary
const formData = new FormData();
formData.append('file', input.files[0]);
formData.append('desc', '用户头像');
fetch('/upload', {
method: 'POST',
body: formData // 不要加 headers: { 'Content-Type': ... }
});
用 fetch 还是 XMLHttpRequest?关键区别在哪
两者都能发 FormData,但行为细节不同:
-
fetch更简洁,原生支持 Promise,但默认不带 cookie(需显式加credentials: 'include') -
XMLHttpRequest提供更细粒度的上传控制,比如监听upload.onprogress(fetch目前无原生进度事件) - 服务端返回非 2xx 状态码时,
fetch不会 reject,需手动检查response.ok;XHR的onerror只在网络失败时触发,状态码错误仍走onload - 大文件上传建议用
XMLHttpRequest,方便加进度条和断点续传逻辑
const xhr = new XMLHttpRequest();
xhr.upload.onprogress = (e) => {
if (e.lengthComputable) {
console.log(`${(e.loaded / e.total * 100).toFixed(1)}%`);
}
};
xhr.open('POST', '/upload');
xhr.send(formData);常见错误与兼容性注意点
很多上传失败不是代码逻辑问题,而是环境或配置疏漏:
- 400 错误:后端没正确解析
multipart/form-data,比如 Express 缺少multer,Koa 缺少@koa/multer - 413 错误:Nginx/Apache 默认限制了请求体大小,需调大
client_max_body_size或LimitRequestBody - 跨域上传失败:后端必须响应
Access-Control-Allow-Origin,且若带 cookie,还需Access-Control-Allow-Credentials: true和前端credentials: 'include' - Safari 对
input[type="file"]的click()触发有限制(需用户手势上下文),不能在异步回调里直接调用input.click() - IE11 不支持
fetch,需用XMLHttpRequest或引入 polyfill
真正难的从来不是“怎么传”,而是服务端能否稳定接收、校验、存储,并在出错时给前端明确反馈。前端上传逻辑越简单,越依赖后端接口设计的健壮性。











