通过FormData添加多个参数、二进制流上传文件有较多坑位,其中reqwest需要设置processData为false,避免data序列化。针对fetch请求除了避免序列化以外,还需要注意去除请求头中的Content-Type,这是因为使用formData格式的数据是经过了 multipart/form-data 算法编码同时以 utf-8 作为显示字符编码。mime-type(Content-Type) 是 multipart/form-data 和 运行 multipart/form-data 算法生成的boundary=xxx串联在一起的字符串。
FormData的使用
FormData
接口提供了一种表示表单数据的键值对 key/value 的构造方式,并且可以轻松的将数据通过XMLHttpRequest.send()
方法发送出去。如果送出时的编码类型被设为multipart/form-data
,它会使用和表单一样的格式。FormData提供了很多方法,这里主要使用到了FormData.append()
向 FormData 中添加新的属性值。
1 | const formData = new FormData(); |
reqwest的简介
reqwest
和普通的 ajax
发送很像,主要用来进行浏览器异步HTTP请求。具体的使用方式见参考文章链接。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16static uploadFile(data) {
return new Promise((resolve, reject) => {
reqwest({
url: `${url}/api/uploadFile`,
method: 'post',
processData: false, // 当设置为true的时候,jquery ajax 提交的时候不会序列化 data,而是直接使用data
data, // 上面的formData
success: (resp) => {
resolve(resp)
},
error: (err) => {
reject(err)
},
});
})
}
使用 fetch
上传二进制文件
踩坑点:
data不可序列化,相当于上面的
processData: false
不可设置
"Content-Type": "multipart/form-data"
当给请求设置以上头部时,浏览器却抛出了 500 同时报给我这么一个错误:Error: Multipart: Boundary not found
,这是因为 post
请求上传文件的时候是不需要自己设置 Content-Type
,会自动给你添加一个 boundary
,用来分割消息主体中的每个字段,如果这个时候自己设置了 Content-Type
, 服务器就不知道怎么分割各个字段,因此就会报错。
补充:
W3C 的文档,文档里面是这样介绍 xhr 上传 FormData 类型的数据的:
Let the request entity body be the result of running the multipart/form-data encoding algorithm with data as form data set and with utf-8 as the explicit character encoding.
Let mime type be the concatenation of “multipart/form-data;”, a U+0020 SPACE character, “boundary=”, and the multipart/form-data boundary string generated by the multipart/form-data encoding algorithm.
大致意思就是请求的实体数据是经过了 multipart/form-data
算法编码同时以 utf-8 作为显示字符编码。mime-type(Content-Type)
是multipart/form-data
和 运行 multipart/form-data
算法生成的 boundary=xxx
串联在一起的字符串。所以使用 FormData 就自动给我们规定了这些内容,不需要我们自己再去指定了。
基于以上我们可以对 fetch
进一步修改:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39static post(params) {
let url = "";
let _params = params;
if (arguments.length > 1) {
url = [0];
_params = [1];
} else {
url = params.url;
_params = params;
}
let createBody = {
body: JSON.stringify(_params.body),
mode: "cors",
credentials: "include",
method: "POST",
headers: Object.assign({
"Content-Type": "application/json"
}, params.headers ? params.headers : {}),
redirect: "follow",
referrer: "no-referrer",
_cache: Object.assign(
{
forceFetch: false,
isCahce: false,
cacheTime: 259200000
},
params._cache
),
..._params.option
}
/**
* 如果是formData格式数据,去除headers以及传递非序列化的data
*/
if (Object.prototype.toString.call(_params.body) === '[object FormData]') {
createBody.body = _params.body;
delete createBody["headers"]["Content-Type"]
}
return Fetch.create(url, createBody);
}