導(dǎo)出現(xiàn)狀
- 大型項目,有文件管理窑眯,前臺導(dǎo)出請求會被后臺寫入到文件管理屏积,可能有權(quán)限和記錄需求
- 一般項目,直接返回流磅甩,但通常會做為兩個接口炊林。前端調(diào)用導(dǎo)出校驗成功后,再調(diào)用導(dǎo)出流接口
- 當(dāng)前方案:后端查詢直接成功直接返回blob卷要,異常返回json渣聚,后臺返回值類型不一致也會報錯
Q:問題
后端成功會返回流独榴,失敗返回json,但因為 axios 請求時聲明了
responseType: 'blob'
奕枝,所以返回值會被處理為blob棺榔,通過導(dǎo)出方法,會將json直接導(dǎo)出為 Excel文件名處理隘道,以方法傳入為優(yōu)先級症歇,若無取 Content-Disposition 返回 fileName,若無取默認(rèn)值
A:答案
- 若后臺返回不是成功谭梗,給出報錯提示忘晤,而不是直接導(dǎo)出為 Excel(默認(rèn)是這樣子)
- 獲取到后臺 Header 的 Content-Disposition,作為導(dǎo)出文件名稱
S:方案
-
獲取到后臺 Header 的 Content-Disposition激捏,作為導(dǎo)出文件名稱
- 主要是后臺調(diào)整设塔,java為例。首先要設(shè)置header缩幸,因為返回流就不會返回json了
- 其次是要設(shè)置response header 暴露給前端訪問壹置。不設(shè)置在瀏覽器查看有,js訪問會為空
備注:設(shè)置需要在 write 前賦值
response.reset(); // 重置輸出流
response.setContentType("application/vnd.ms-excel;charset=UTF-8"); //通知客服文件的MIME類型
//設(shè)置要下載的文件的名稱: 若是中文需要轉(zhuǎn)碼, java亂碼為 ?????
response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(sheetName, "utf-8"));
// 服務(wù)端要在header設(shè)置Access-Control-Expose-Headers, 前端才能正常獲取到
response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
- 若后臺返回不是成功表谊,給出報錯提示钞护,而不是直接導(dǎo)出為 Excel(默認(rèn)是這樣子)- 文件名稱處理
2.1 在返回 aop 處理導(dǎo)出
// 響應(yīng)攔截器
http.interceptors.response.use(
async response => {
function formatResponse(insertFragment) {
_interceptorsLoadingAndMessage();
insertFragment && insertFragment();
return response.data;
}
// 文件Excel導(dǎo)出: NOTE: 處理請求聲明的blob是否為json
if (response.config.conf[KEY_EXPORT_TYPE]) {
const res = await _fileToJson(response.data);
if (!res.message) {
response.data = res.data;
} else {
// 處理文件名稱: <詳見 http://www.reibang.com/p/9352c68a0635>
let fileName;
try {
const disposition = response.headers["content-disposition"];
fileName = decodeURIComponent(disposition.split("fileName=")[1]); // 中文需要轉(zhuǎn)碼 (前端亂碼為百分號形式)
} catch (error) {
fileName = "導(dǎo)出文件";
}
if (!fileName.includes(".xls")) fileName += ".xls";
return { data: formatResponse(), fileName }; // 格式化輸出
}
}
const msg = response.data.msg || response.data.message;
const code = Number(response.data.code);
if (code === CODE_SUCCESS) {
return formatResponse(() => {
// 需要show成功Message信息
if (response.config.conf[KEY_SHOW_MESSAGE])
progress.showSuccessMessage(msg);
});
}
// 請求報錯: 顯示成功的 Toast 提示
if (code === CODE_FINALLY) {
return Promise.reject(
formatResponse(() => progress.showSuccessMessage(msg))
);
}
// 登錄失效跳轉(zhuǎn)登錄頁面. NOTE: 注意可能導(dǎo)致循環(huán)調(diào)用
if (code === CODE_INVALID) {
progress.invalidToken(msg || TIP_INVALID_TOKEN);
}
// 請求結(jié)束loading和error處理
return _interceptorsLoadingAndMessage(
new Error(msg || TIP_ERROR_MESSAGE),
!response.config.conf[KEY_NO_ERROR_TIP]
);
},
error => {
return _interceptorsLoadingAndMessage(error);
}
);
2.2 將返回值格式為json,驗證返回是否為流類型
// 將blob對象轉(zhuǎn)化為json(文件類型調(diào)用ajax 取后端的返回值做特殊處理)
function _fileToJson(file) {
let data = {},
message = "";
function formatReturn() {
return { data, message };
}
return new Promise(resolve => {
const reader = new FileReader();
reader.onload = res => {
const { result } = res.target; // 得到字符串
try {
// 解析成json對象
data = JSON.parse(result);
} catch (err) {
message = err.message || err;
}
resolve(formatReturn());
}; // 成功回調(diào)
reader.onerror = err => {
message = err.message || err;
resolve(formatReturn());
}; // 失敗回調(diào)
reader.readAsText(new Blob([file]), "utf-8"); // 按照utf-8編碼解析
});
}
- 通用前端 blob 導(dǎo)出方式
/** 4.Excel:報表blob導(dǎo)出 */
request.exportExcel_blob = function(res = {}, fileName) {
const blob = new Blob([res.data], {
type: 'application/vnd.ms-excel;charset=UTF-8',
});
const link = document.createElement('a');
link.style.display = 'none';
link.href = URL.createObjectURL(blob);
link.download = fileName || res.fileName; // 下載后文件名: 攔截器處理 content-disposition, 傳入優(yōu)先
document.body.appendChild(link);
link.click();
URL.revokeObjectURL(link.href);
document.body.removeChild(link);
};
- **至此就可以封裝出通用的Excel接口
/** 5.Excel:報表blob導(dǎo)出 */
request.exportExcel = async function(url, params = {}, fileName) {
const res = await request.postData(url, params, {
[KEY_EXPORT_TYPE]: 'blob',
});
return this.exportExcel_blob(res, fileName);
};
2021.03.05 真是一個執(zhí)著的人爆办,為了這個問題研究了差不多1天难咕,記錄一下學(xué)習(xí)真好,開心 ~