前端根据后端返回的excel二进制文件流进行导出下载

打印 上一主题 下一主题

主题 1853|帖子 1853|积分 5559

需求

在vue2中,后端接口返回一个文件流,前端实现excel文件流导出下载功能。
解决方案

利用axios请求后端接口,把后端返回的blob文件流转为一个临时在线url,然后利用a标签实现导出下载功能。
详细实现步骤

1、封装axios请求拦截器
关键代码

  1. import axios from 'axios'
  2. import Vue from 'vue'
  3. import store from '@/store/index'
  4. import { Notification } from 'element-ui'
  5. import logger from '@/plugins/logger'
  6. // 基础路由
  7. axios.defaults.baseURL = 'http://xxx:端口号' // 此处为后端接口服务器IP或域名地址
  8. // 设置请求超时为5分钟
  9. axios.defaults.timeout = 300000
  10. // 不需要token验证的白名单
  11. const noAuthWhiteList = [
  12.   '/api/oa/auth',
  13. ]
  14. // 判断是否在白名单中
  15. const isInWhiteList = (url) => {
  16.   return noAuthWhiteList.some(whiteUrl => url.includes(whiteUrl))
  17. }
  18. axios.interceptors.request.use(
  19.   (config) => {
  20.     // 如果请求配置中明确指定了noAuth,或者url在白名单中,则不添加token
  21.     if (!config.noAuth && !isInWhiteList(config.url)) {
  22.       const token = store.state.main.token
  23.       token && (config.headers.Authorization = token)
  24.     }
  25.    
  26.     const catalogId = store.state.main.catalogId
  27.     catalogId && (config.headers.catalogId = catalogId)
  28.     return config
  29.   },
  30.   (error) => {
  31.     Notification.error({ title: '网络请求错误', message: error })
  32.     logger.error(error)
  33.     return Promise.reject(error)
  34.   }
  35. )
  36. axios.interceptors.response.use(
  37.   function (response) {
  38.     // 如果是文件流,直接返回
  39.     if (response.config.responseType === 'blob') {
  40.       return response;
  41.     }
  42.    
  43.     if (response.data.code !== 200) {
  44.       Notification.error({
  45.         title: '请求失败',
  46.         message: response.data.message || '数据填写错误,请查看错误日志详情',
  47.       })
  48.       return Promise.reject(response)
  49.     }
  50.     return Promise.resolve(response)
  51.   },
  52.   function (error) {
  53.     if (error.code === 'ECONNABORTED' && error.message.includes('timeout')) {
  54.       Notification.error({
  55.         title: '请求超时',
  56.         message: '请求已超时,请稍后重试。',
  57.       })
  58.     } else if (error.response && error.response.status === 401) {
  59.       // 1.接口401的时候,需要调用refresh token接口
  60.       if (localStorage.getItem('firstRefresh')) {
  61.         // localStorage.clear()
  62.         localStorage.setItem('TOKEN', '')
  63.         location.reload()
  64.       } else {
  65.         localStorage.setItem('firstRefresh', true)
  66.         store
  67.           .dispatch(GET_REFRESH_TOKEN, {
  68.             refreshToken: store.state.main.refreshToken,
  69.           })
  70.           .then((resp) => {
  71.             localStorage.setItem('TOKEN')
  72.             location.reload()
  73.           })
  74.           .catch((error) => {
  75.             localStorage.setItem('TOKEN')
  76.             location.reload()
  77.           })
  78.       }
  79.     } else {
  80.       // 处理文件流错误响应
  81.       if (error.response && error.response.config.responseType === 'blob') {
  82.         // 转换blob错误信息
  83.         const reader = new FileReader();
  84.         reader.onload = () => {
  85.           try {
  86.             const errorData = JSON.parse(reader.result);
  87.             Notification.error({
  88.               title: '导出失败',
  89.               message: errorData.message || '文件导出失败',
  90.             });
  91.           } catch (e) {
  92.             Notification.error({
  93.               title: '导出失败',
  94.               message: '文件导出失败',
  95.             });
  96.           }
  97.         };
  98.         reader.readAsText(error.response.data);
  99.       } else {
  100.         Notification.error({
  101.           title: '请求失败',
  102.           message: error.message,
  103.         });
  104.       }
  105.     }
  106.     logger.error(error)
  107.     return Promise.reject(error)
  108.   }
  109. )
  110. function plugin(Vue, axios) {
  111.   if (plugin.installed) {
  112.     return
  113.   }
  114.   plugin.installed = true
  115.   Object.defineProperties(Vue.prototype, {
  116.     $axios: {
  117.       get() {
  118.         return axios
  119.       },
  120.     },
  121.   })
  122. }
  123. Vue.use(plugin, axios)
  124. export default Vue.prototype.$axios
复制代码
2、引入axios依靠
  1. import axios from '@/plugins/axios'
复制代码
3、在api.js文件中请求接口,创建a标签实现下载导出
  1. // 导出
  2. export async function exportFile(params) {
  3.     const res = await axios.post('/api/file/export', params, {
  4.         responseType: 'blob',
  5.         headers: {
  6.             'Content-Type': 'application/json'
  7.         }
  8.     });
  9.    
  10.     // 从响应头中获取文件名
  11.     const fileName = res.headers['content-disposition']
  12.         ? decodeURIComponent(res.headers['content-disposition'].split('filename=')[1])
  13.         : `导出模板_${new Date().getTime()}.xlsx`;
  14.    
  15.     // 创建Blob对象
  16.     const blob = new Blob([res.data], {
  17.         type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  18.     });
  19.    
  20.     // 创建下载链接
  21.     const link = document.createElement('a');
  22.     link.href = window.URL.createObjectURL(blob);
  23.     link.download = fileName;
  24.     link.click();
  25.     window.URL.revokeObjectURL(link.href);
  26. }         
复制代码
写在最后

利用时把接口/api/file/export换为本身的接口地址即可,记住一定要加返回范例responseType: ‘blob’,否则可能造成文件无法正常打开。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

石小疯

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表