对使用fetch、reqwest上传文件的方法总结

二进制流上传文件的注意事项

Posted by Xshellv on 2020-12-17

通过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
2
3
4
5
6
7
8
const formData = new FormData();
// fileList 是我们要上传的xlsx文件。
fileList.forEach(file => {
formData.append('file', file);
});
formData.append('eid', eid);
formData.append('channel', channel);
formData.append('messageCode', messageCode);

reqwest的简介

reqwest 和普通的 ajax 发送很像,主要用来进行浏览器异步HTTP请求。具体的使用方式见参考文章链接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static 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
39
static 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);
}

参考文章