解決Yapi 插件運行不支持文件上傳的問題解決

背景

chrome 在 73 版本後,限制了 content-script 跨域請求目前只有一個解決辦法,廢棄 content-script 跨域請求,使用background.js 執行跨域請求,但這樣有個最大的問題是沒法支持文件上傳javascript

問題的背景如上。原先的yapi插件支持兩種方式, 第一個是在content-script中進行請求,另一個則是background進行請求,可是background須要依賴於content-script將請求的內容傳遞給到background中。java

遇到的坑

一開始以爲這個問題其實不難解決,由於在content-script已經將全部的請求內容都獲取到了,包括上傳的文件(formdata格式)因此只要將內容傳遞給到background中便可chrome

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後發現以下: api

image
消息必須是可被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進行區別是不是文件類型。app

background.js中的處理post

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進行存儲對應的數據ui

如此便可解決文件經過background進行文件上傳的問題this

相關文章
相關標籤/搜索