javascript如何实现文件上传_有哪些相关API?

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

JavaScript 本身不能直接读写本地文件系统,但可以通过浏览器提供的标准 API 实现用户主动选择并上传文件。核心是 FileFileListFormDataXMLHttpRequestfetch 的组合使用。

如何用 获取用户选中的文件

这是最常见、最可控的入口。用户点击后触发 change 事件,从 event.target.files 中拿到 FileList 对象。

注意:files 是只读的类数组对象,不是真正的 Array,不能直接用 map 等方法 —— 需转成数组再操作。

  • multiple 属性决定是否允许多选(
  • accept 属性可限制类型(如 accept="image/*,.pdf"),但只是提示,不阻止用户绕过
  • files[0] 是第一个 File 实例,它继承自 Blob,有 namesizetypelastModified 等属性
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

  • 上传单个文件:formData.append('file', file)
  • 上传多个同名字段:formData.append('files', file1)formData.append('files', file2)
  • 上传时附带其他参数:formData.append('userId', '123')
  • 不要手动设置 Content-Type 头 —— fetchXHR 发送 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.onprogressfetch 目前无原生进度事件)
  • 服务端返回非 2xx 状态码时,fetch 不会 reject,需手动检查 response.okXHRonerror 只在网络失败时触发,状态码错误仍走 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_sizeLimitRequestBody
  • 跨域上传失败:后端必须响应 Access-Control-Allow-Origin,且若带 cookie,还需 Access-Control-Allow-Credentials: true 和前端 credentials: 'include'
  • Safari 对 input[type="file"]click() 触发有限制(需用户手势上下文),不能在异步回调里直接调用 input.click()
  • IE11 不支持 fetch,需用 XMLHttpRequest 或引入 polyfill

真正难的从来不是“怎么传”,而是服务端能否稳定接收、校验、存储,并在出错时给前端明确反馈。前端上传逻辑越简单,越依赖后端接口设计的健壮性。