Skip to content

传输上传数据

文件选择完成后,下一步通常不是直接“把文件变量发出去”,而是先把它组织成请求体。

上传文件时最常见的请求体:FormData

浏览器里最常见的文件上传数据结构是 FormData

它适合表示:

  • 文件字段
  • 普通文本字段
  • 一组表单项
js
const formData = new FormData()
formData.append('file', file)
formData.append('username', 'exampleUser')

为什么文件上传通常不用 JSON

如果请求里带的是二进制文件,直接用 JSON 通常不合适。

文件上传场景里更常见的是:

  • multipart/form-data

这正是 FormData 最擅长处理的格式。

一个基础例子

js
const fileInput = document.getElementById('fileInput')
const file = fileInput.files[0]

const formData = new FormData()
formData.append('file', file)
formData.append('username', 'exampleUser')
formData.append('email', 'user@example.com')

这时同一个请求体里既可以放文件,也可以放普通字段。

可以怎么发送

fetch

js
fetch('/upload', {
  method: 'POST',
  body: formData,
})

用 XHR

js
const xhr = new XMLHttpRequest()
xhr.open('POST', '/upload')
xhr.send(formData)

一个高频注意点:不要手动设置 Content-Type

这是文件上传里最常见的坑之一。

当请求体是 FormData 时,不要手动写:

js
headers: {
  'Content-Type': 'multipart/form-data'
}

因为浏览器会自动补完整的 Content-Type,包括必要的 boundary

如果手动写死,反而可能让服务端无法正确解析。

传统表单提交也是同一类思路

浏览器原生表单上传本质上也是类似模型:

html
<form enctype="multipart/form-data">
  <input type="file" name="fileInput" />
  <button type="submit">上传文件</button>
</form>

区别只是:

  • 原生表单由浏览器自动提交
  • FormData + fetch/XHR 更适合前端主动控制流程

什么时候不是 FormData

如果上传的不是文件,而只是普通结构化数据,通常还是优先 JSON:

js
fetch('/api/user', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ name: 'Lee' }),
})

所以可以先简单记成:

  • 有文件:优先 FormData
  • 纯业务数据:优先 JSON

使用建议

  • 上传文件时,优先用 FormData 组织请求体。
  • 文件和附加字段可以一起放进同一个 FormData
  • 发送 FormData 时,不要手动设置 Content-Type
  • 如果只是普通对象数据,不要为了统一而强行改成 FormData

基于 MIT 许可发布