闽公网安备 35020302035485号
很多时候,在 post 提交数据时我们常采用 application/json、application/x-www-form-urlencoded 等类型,也确实能够覆盖到大部分的场景,但是有一些场景下,比如文件上传的时候,就不算是好的解决方案了,application/json 作为请求头 Content-Type 字段值时,表示告知服务端参数是序列化后的 JSON 字符串,所以一般在传参时都会用 JSON.stringify 序列化一下,且浏览器对 JSON.stringify API 支持程度比较高。
但是 JSON.stringify 在转换某一些数据结构时会出问题,比如 会丢失 function 类型的参数、循环引用时会报错、Blob /File 对象会被转化成 {} 等等,,可以参考 为何不推荐使用 JSON.stringify 做深拷贝,不过 JSON.stringify 还有第三个参数,有兴趣的同学可以去了解下,这是其一,其二,有同学要说了,如果要是图片那可以转换成 base64 格式进行上传解决,这种方式虽然可行,但是转换成 base64 格式需要很多字符,占用很多资源,而且很长,不便于阅读,另外就是服务端接收到这个参数还得解析,很麻烦,此时,FormData 就可用上了。


const specialFileType = ['Blob', 'File'];
// 堆代码 duidaima.com
function formatData (_data) {
const data = new window.FormData()
for (const key in _data) {
let value = _data[key]
if (_data[key] instanceof Object && !specialFileType.includes(_data[key].constructor.name)) {
value = JSON.stringify(_data[key])
}
data.append(key, value)
}
return data
}

cosnt View = () => {
const [fileA, setFileA] = useState(null);
const [fileB, setFileB] = useState(null);
const handleClick = () => {
console.log('fileA:', fileA)
console.log('fileB:', fileB)
const p = {
a: { a1: 11, a2: 22 },
b: [1,2,3],
c: 123,
d: fileA[0],
e: fileB[0],
}
const data = formatData(p);
axios({
method: 'POST',
url: '/aa',
data,
// headers: {
// 'content-type': 'multipart/formdata'
// },
})
}
return <div>
<div onClick={handleClick}>发送请求</div>
<input
type='file'
onChange={(a) => {
const v = a.target.files;
setFileA(v);
}}
/>
<input
type='file'
onChange={(a) => {
const v = a.target.files;
setFileB(v);
}}
/>
</div>
}
可以看到 每一个参数之间都有一个 ------WebKitFormBoundary *** 区分开,这实际上是 FormData 的规范标志,后面的字符串是浏览器帮我们自动创建的,以 ------WebKitFormBoundary *** 作为分隔符,也作为开始和结尾,其内容主要有 Content-Disposition、Content-Type 等,其中 Content-Disposition 是必选项, name 属性代表着表单元素的 key,filename 则是上传文件的名称,也可以使用 FormData 第三个参数更改 。
另外,我在发送请求时,并没有更改请求头里面的 Content-Type,但实际上我们看到的是正确的 multipart/form-data,这是因为现在的浏览器比较智能,当客户端未设置请求头的 Content-Type 时,请求参数为对象时,某一些浏览器会自动帮我们在 请求头中添加 Content-Type: text/plain,如果传输的数据是 FormData,也会自动帮我们加上 Content-Type: multipart/form-data 等,可能不同浏览器表现行为不一样,但是最好的方式就是客户端与服务端约定好 Content-Type 类型,固定传递。