chrome 在 73 版本后,限制了 content-script 跨域请求目前只有一个解决办法,废弃 content-script 跨域请求,使用background.js 执行跨域请求,但这样有个最大的问题是无法支持文件上传
问题的背景如上。原先的yapi插件支持两种方式, 第一个是在content-script中进行请求,另外一个则是background进行请求,但是background需要依赖于content-script将请求的内容传递给到background中。
一开始觉得这个问题其实不难解决,因为在content-script已经将所有的请求内容都获取到了,包括上传的文件(formdata格式)所以只要将内容传递给到background中即可
function sendAjaxByBack(id, req, successFn, errorFn) {
successFns[id] = successFn;
errorFns[id] = errorFn;
connect.postMessage({
id: id,
req: req
});
}
只要将文件的formdata内容赋值到req中即可,结果发现在background中接收到的formdata的数据为空。翻阅了下google关于postMessage的api后发现如下:
消息必须是可被JSON序列化的,所以就是为什么formData的数据变成空的原因了。
考虑了下,并不是说file的对象一定要用formdata已经包装传递,是否可以变成字符串的形式呢,这个时候我们可以想到可以把文件转换成base64进行传递,然后再解析回原先的文件对象即可了。
所以代码如下
function sendAjaxByBack(id, req, successFn, errorFn) {
successFns[id] = successFn;
errorFns[id] = errorFn;
if (req.headers['Content-Type'] === 'multipart/form-data') {
var formDatas = []
if (req.data) {
for (var name in req.data) {
formDatas.push({name, value: req.data[name], is_file: false});
}
}
if (req.files) {
let allPromise = [];
for (var name in req.files) {
let fileTransfer = new Promise(function (resolve, reject){
var files = document.getElementById(req.files[name]).files;
let file = files[0]
var reader = new FileReader();
reader.name = name;
reader.fileName = file.name;
reader.onload = function () {
resolve({name: this.name, value: this.result,is_file: true, fileName: this.fileName});
}
reader.readAsDataURL(file);
})
allPromise.push(fileTransfer);
}
Promise.all(allPromise).then(function(result){
formDatas = formDatas.concat(result);
req.formDatas = formDatas;
connect.postMessage({
id: id,
req: req,
});
})
}else {
req.formDatas = formDatas;
connect.postMessage({
id: id,
req: req,
});
}
} else {
connect.postMessage({
id: id,
req: req
});
}
}
针对文件的发送转换成base64进行存储分别依次放到formdata的一个数组中,这里要区别部分是文件的类型,部分是普通的字符串,所以我们这里通过一个is_file进行区别是否是文件类型。
background.js中的处理
if (!req.headers['Content-Type'] || req.headers['Content-Type'] == 'application/x-www-form-urlencoded') {
req.headers['Content-Type'] = 'application/x-www-form-urlencoded';
req.data = formUrlencode(req.data);
} else if (req.headers['Content-Type'] === 'multipart/form-data') {
delete req.headers['Content-Type'];
let formDatas = new FormData();
for (var item of req.formDatas) {
if (item.is_file) {
formDatas.append(item.name, dataURLtoFile(item.value, item.fileName))
}else {
formDatas.append(item.name, item.value);
}
}
req.data = formDatas;
} else if (typeof req.data === 'object' && req.data) {
req.data = JSON.stringify(req.data);
}
针对content-Type为multipart/form-data的处理,声明一个formdata进行存储对应的数据
如此即可解决文件通过background进行文件上传的问题