fetch下載檔案--統一攔截報表匯出檔案
阿新 • • 發佈:2018-12-20
做外賣報表匯出功能,前端使用fetch請求,後端在正常情況向會返回csv檔案,在異常時會返回對應的異常碼;前端的請求都是使用request.js做了統一攔截和錯誤提示,但是不支援檔案下載,於是對原有內容稍作改造,支援檔案下載。
前端是怎麼實現檔案下載的?
// 使用fetch傳送請求並拿到返回值 const response = await fetch(newUrl, responseBody); // 將檔案流轉為blob物件,並獲取本地檔案連結 response.blob().then((blob) => { const a = window.document.createElement('a'); const downUrl = window.URL.createObjectURL(blob);// 獲取 blob 本地檔案連線 (blob 為純二進位制物件,不能夠直接儲存到磁碟上) const filename = response.headers.get('Content-Disposition').split('filename=')[1].split('.'); a.href = downUrl; a.download = `${decodeURI(filename[0])}.${filename[1]}`; a.click(); window.URL.revokeObjectURL(downUrl); });
實際場景中需要區分返回的是否是檔案,檔案走下載方法,資料走原有的request.js中定義的方法,這裡需要使用響應頭的Content-Type
來判斷,這裡後端直返回CSV檔案所以使用response.headers.get('Content-Type').indexOf('application/msexcel') > -1
來判斷是否為檔案下載(響應頭與後端約定好了)。
if (response.headers.get('Content-Type').indexOf('application/msexcel') > -1) { response.blob().then( 執行檔案下載 ) } else { response.json(); 原有資料處理方法 }
request.js
import fetch from 'dva/fetch';
import { stringify } from 'qs';
import { message } from 'antd';
import { getFormData } from './index';
/**
* Requests a URL, returning a promise.
*
* @param {string} url The URL we want to request
* @param {object} [options] The options we want to pass to "fetch"
* @return {object} An object containing either "data" or "err"
*
/* message.config({
top: 10,
duration: 5,
getContainer: () => window.document.getElementById('root'),
});*/
export default async function request(url, options) {
let newOptions;
let newUrl = url;
let responseBody = {};
let data = {};
const { body, params } = options;
// 判斷請求型別
if ((!(typeof body === 'string')) && body) {
newOptions = Object.assign({}, options, { body: getFormData(body) });
responseBody = {
credentials: 'same-origin',
...newOptions,
};
} else {
newOptions = options;
responseBody = {
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json',
},
...newOptions,
};
}
// get
if (params) {
newUrl += `?${stringify(params)}`;
}
const response = await fetch(newUrl, responseBody);
// 報表匯出 Content-Type為application/msexcel時,為檔案流,進行下載操作
if (response.headers.get('Content-Type').indexOf('application/msexcel') > -1) {
response.blob().then((blob) => {
const a = window.document.createElement('a');
const downUrl = window.URL.createObjectURL(blob);// 獲取 blob 本地檔案連線 (blob 為純二進位制物件,不能夠直接儲存到磁碟上)
const filename = response.headers.get('Content-Disposition').split('filename=')[1].split('.');
a.href = downUrl;
a.download = `${decodeURI(filename[0])}.${filename[1]}`;
a.click();
window.URL.revokeObjectURL(downUrl);
});
return data;
}
if (response.status !== 200) {
message.error('網路或伺服器異常!');
return {
data: {
code: response.status,
},
};
}
data = await response.json();
if (data.code !== '200') {
// 狀態碼不為304或302的時候報錯
if (data.code !== '304' && data.code !== '302') {
console.log(data.msg);
message.error(data.msg);
}
// 狀態碼為304的時候,請求登入介面
if (data.code === '304') {
request('/login', {
method: 'post',
body: {
...data.data,
redirect_uri: encodeURIComponent(window.location.href),
},
});
}
// 狀態碼為302的時候,進行跳轉
if (data.code === '302') {
window.location = data.msg;
}
}
return { data };
}