解决Yapi 插件运行不支持文件上传的问题解决

背景

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后发现如下:
解决Yapi 插件运行不支持文件上传的问题解决_第1张图片
消息必须是可被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进行文件上传的问题

你可能感兴趣的:(随笔)