背景
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进行文件上传的问题