1. 程式人生 > >fetch下載檔案--統一攔截報表匯出檔案

fetch下載檔案--統一攔截報表匯出檔案

做外賣報表匯出功能,前端使用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 }; }