当导入导出为同一个接口时,会产生什么样的“化学反应”?
業務需求中出現一個接口既有導入功能,又需要有導出功能,接口具體情況如下:
導入excel表格接口中,前端傳遞上傳的excel文檔,后端判斷文檔是否格式正常,如果格式正常,則返回json格式的成功數據,如果格式不成功,則返回錯誤的excel文檔,前端負責把錯誤的excel文檔導出,第三種情況,如果5s內再次導入文檔,則后端返回不能頻繁導入的錯誤json數據。
總結一下:
情況1:前端導入excel表格,觸發請求,后端響應文件,blob格式,導入數據失敗,前端導出文件;
情況2:前端導入excel表格,觸發請求,后端響應json格式,導入數據正常,存儲到數據庫中;
情況3:前端5s內再次導入excel表格,觸發請求,后端響應json格式,導入數據失敗,不能頻繁導入;
情況1:導出excel由于格式問題打不開
導出接口處理如下:
const apiImportExcel = (params) => {return axiosBizcenterMarketingAct.request({url: 'https//www.xxx.com/importExcelOptLessNum',method: 'post',data: params}) }// 上傳excel,失敗下載excel,成功返回msg const uploading = async (data) => {uploadLoading.value = truelet file = data.filelet params = new FormData()params.append('file', file)let res = await apiImportExcel(params)console.log(res, 1234)blobData.value = resisShowDownload.value = trueuploadMessage.value = '處理失敗,請整理表格后重新上傳文檔'uploadLoading.value = falsedialogVisible.value = true }// 導出 const downErrorExcel = () => {let fileName = '錯誤提示.xlsx'if (!blobData.value) {return}const blob = new Blob([blobData.value], { type: 'application/vnd.ms-excel' })const url = URL.createObjectURL(blob)downloadCommon(url, fileName) }// 下載處理 const downloadCommon = (url: string, fileName: string) => {const link = document.createElement('a')link.style.display = 'none'link.href = urllink.setAttribute('download', fileName)document.body.appendChild(link)link.click()document.body.removeChild(link) // 下載完成移除元素 }控制臺輸出為
axios攔截器輸出為
解決問題1
由于后端返回文件時,前端必須設置responseType為blob,否則導出的excel會由于格式問題,打不開;
// 上傳excel const apiImportExcel = (params) => {return axiosBizcenterMarketingAct.request({url: 'https"//www.test.com/importExcelOptLessNum',method: 'post',responseType: 'blob',data: params}) }情況2:導入成功,返回的內容為blob類型,json字符讀取不到
如下圖:控制臺上方為攔截器輸出內容,下方為拿到的data數據
但是當設置了響應格式為blob,則當后端返回json格式時,前端只能讀到blob數據,blob數據看不到里面的內容。
相信大家看到了雖然返回都是blob格式,但是情況1和情況2返回的blob格式的type不一樣。所以可以如下處理:
解決問題2:
處理代碼如下:
// 上傳excel,失敗下載excel,成功返回msg const uploading = async (data) => {uploadLoading.value = truelet file = data.filelet params = new FormData()params.append('file', file)let res = await apiImportExcel(params)console.log(res, 1234)if(res.type === 'text/xml') {isShowDownload.value = trueuploadMessage.value = '處理成功,成功導入'} else {blobData.value = resisShowDownload.value = trueuploadMessage.value = '處理失敗,請整理表格后重新上傳文檔'}uploadLoading.value = falsedialogVisible.value = true }情況3:第三種情況和第二種情況怎么區分?
由于情況2和情況3返回都是json格式的問題,提示用戶接口處理是否成功,所以正常的流程是后端接口返回的什么樣的字符串,前端頁面直接提示用戶接口處理結果,而且目前是三種情況,將來擴展可能接口出現第四種第五種情況,所以使用上面的type判斷,并不合適。
解決問題3:
不設置responseType為blob,會導致excel因為格式問題打不開,所以只能讓接口返回blob格式,那就只能當接口返回類型為text/xml的時候,將blob解析成json格式,然后再讀取json字符串了。
如何解析blob格式呢?下面有三種方法:
// 方法1const getJSONObjectFromBlob = (blob: Blob) => {const reader = new FileReader()reader.readAsArrayBuffer(blob)reader.onload = function () {console.info('reader.result>>>', reader.result) //ArrayBuffer {}//將 ArrayBuffer 轉換成Blobconst buf = new Uint8Array(reader.result as ArrayBuffer)let enc = new TextDecoder('utf-8')let text = enc.decode(buf)try {const obj = JSON.parse(text)return obj} catch (error) {console.error('解析錯誤', error)return error}}}// 方法2const getJSONObjectFromBlob = (blob: Blob) => {const text = await (new Response(blob)).text();try {const obj = JSON.parse(text)return obj} catch (error) {console.error('解析錯誤', error)return error}}// 方法3const getJSONObjectFromBlob = (blob: Blob) => {const text = await blob.text();try {const obj = JSON.parse(text)return obj} catch (error) {console.error('解析錯誤', error)return error}}看到這里,大家可能看到方法2和方法3都是讀取Blob對象原型鏈上的text()方法進行解析,其實text()方法就是根據方法1這樣設計的,不過目前這個方法1有點小瑕疵,大家能夠發現嗎?
方法1中使用FileReader對象去讀取blob,然后通過onload去解析,不知道大家是否了解onload事件,onload是一個異步任務,所以js引擎走到這里不會去執行內部邏輯,而是等函數執行完才去宏任務隊列中取出它,并執行。那函數還有未執行呢?走到這里,函數還沒有返回值,所以大家應該猜到了吧,這里會返回undefined。
那怎么解決呢?大家有沒有想到await和async關鍵字,但是使用這兩個關鍵字,必須要是一個promise,所以把方法1改造成返回promise,然后去讀取json對象,使用await關鍵字等待,promise響應再執行后續操作,就可以啦~~~
代碼如下:
const getTextFromBlob = (blob: Blob): Promise<string> => {//將Blob 對象轉換成 ArrayBufferreturn new Promise((resolve, reject) => {const reader = new FileReader()reader.readAsArrayBuffer(blob)reader.onload = function () {console.info('reader.result>>>', reader.result) //ArrayBuffer {}// 經常會遇到的異常 Uncaught RangeError: byte length of Int16Array should be a multiple of 2// const buf = new int16array(reader.result);// console.info(buf);//將 ArrayBuffer 轉換成Blobconst buf = new Uint8Array(reader.result as ArrayBuffer)let enc = new TextDecoder('utf-8')resolve(enc.decode(buf))}reader.onerror = function (e) {console.error('轉換成ArrayBeffer失敗')reject(e)}}) } // 解析 text/xml類型的blob, 成為JSON對象(失敗則是json字符串) export async function getJSONObjectFromBlob(blob: Blob): Promise<Record<string, unknown> | unknown> {let text = ''try {text = await getTextFromBlob(blob)const obj: Record<string, unknown> = JSON.parse(text)return obj} catch (error) {return error} }好了,為了能夠通熟易懂,我還是使用了最簡單的方法,代碼如下:
const apiImportExcel = (params) => {return axiosBizcenterMarketingAct.request({url: 'https"//www.test.com/importExcelOptLessNum',method: 'post',data: params}) } // 解析application/json類型的blob, 成為JSON對象(失敗則是json字符串)const getJSONObjectFromBlob = (blob: Blob) => {const text = await blob.text();try {const obj = JSON.parse(text)return obj} catch (error) {console.error('解析錯誤', error)return error}}// 上傳excel,失敗下載excel,成功返回msgconst uploading = async (data) => {uploadLoading.value = truelet file = data.filelet params = new FormData()params.append('file', file)let res = await apiImportExcel(params)if (res.type === 'application/json') {// 讀取BlobgetJSONObjectFromBlob(res).then((res) => {uploadMessage.value = res.msg})} else {blobData.value = resisShowDownload.value = trueuploadMessage.value = '處理失敗,請整理表格后重新上傳文檔'}uploadLoading.value = falsedialogVisible.value = true}const downloadCommon = (url: string, fileName: string) => {const link = document.createElement('a')link.style.display = 'none'link.href = urllink.setAttribute('download', fileName)document.body.appendChild(link)link.click()document.body.removeChild(link) // 下載完成移除元素}寫到這里,有人會想到使用axios去請求接口會出現這種情況,那使用fetch呢?window對象自帶的請求接口的方法,目前兼容性已經滿足大部分瀏覽器了,所以我又做了以下內容
使用fetch請求
我們直接改造上面的uploading函數就行,說干就干
// 上傳excel,失敗下載excel,成功返回msgconst uploading = async (data) => {uploadLoading.value = truelet file = data.filelet params = new FormData()params.append('file', file)window.fetch("https//www.xxx.com/importExcelOptLessNum", {method: 'post',body: params,headers: {'Authorization': accessToken,}}).then(res => {console.log(res, '----1---')return res.json();// return res.blob();}).tnen(res => {console.log(res, '0----3---------')blobData.value = res;isShowDownload.value = trueuploadMessage.value = '處理失敗,請整理表格后重新上傳文檔';}).catch(err => {console.error(err)});uploadLoading.value = falsedialogVisible.value = true}結果就是不行,從上圖中可以看到,fetch確實不需要在請求頭這里設置響應體類型,但是當數據響應給客戶端時,必須要調用Response中的json()方法或者blob()方法,但在這之前,無法判斷res是什么數據類型。
總結一下:
雖然情況1和情況2兩次的blob的類型不一致,一個是application/vnd.ms-excel類型,一個是text/xml,但是由于第三種情況的介入,必須要獲取到后端返回的json格式數據顯示。
設置了responseType為blob類型,接口返回的數據如下:
沒有設置responseType時,接口返回的數據如下:
目前是 解析 text/xml類型的blob, 成為JSON對象。
更多關于Blob對象的知識,可以點擊鏈接查看:https://developer.mozilla.org/zh-CN/docs/Web/API/Blob
如果大家有什么更好的處理方法,歡迎評論區討論~~~
總結
以上是生活随笔為你收集整理的当导入导出为同一个接口时,会产生什么样的“化学反应”?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: kafka 维护消费状态跟踪的方法和消费
- 下一篇: Android中动画类别及优缺点,安卓培