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

需求

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

解决方案

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

具体实现步骤

1、封装axios请求拦截器

关键代码

javascript 复制代码
import axios from 'axios'
import Vue from 'vue'
import store from '@/store/index'
import { Notification } from 'element-ui'
import logger from '@/plugins/logger'
// 基础路由
axios.defaults.baseURL = 'http://xxx:端口号' // 此处为后端接口服务器IP或域名地址

// 设置请求超时为5分钟
axios.defaults.timeout = 300000

// 不需要token验证的白名单
const noAuthWhiteList = [
  '/api/oa/auth',
]

// 判断是否在白名单中
const isInWhiteList = (url) => {
  return noAuthWhiteList.some(whiteUrl => url.includes(whiteUrl))
}

axios.interceptors.request.use(
  (config) => {
    // 如果请求配置中明确指定了noAuth,或者url在白名单中,则不添加token
    if (!config.noAuth && !isInWhiteList(config.url)) {
      const token = store.state.main.token
      token && (config.headers.Authorization = token)
    }
    
    const catalogId = store.state.main.catalogId
    catalogId && (config.headers.catalogId = catalogId)
    return config
  },
  (error) => {
    Notification.error({ title: '网络请求错误', message: error })
    logger.error(error)
    return Promise.reject(error)
  }
)

axios.interceptors.response.use(
  function (response) {
    // 如果是文件流,直接返回
    if (response.config.responseType === 'blob') {
      return response;
    }
    
    if (response.data.code !== 200) {
      Notification.error({
        title: '请求失败',
        message: response.data.message || '数据填写错误,请查看错误日志详情',
      })

      return Promise.reject(response)
    }
    return Promise.resolve(response)
  },
  function (error) {
    if (error.code === 'ECONNABORTED' && error.message.includes('timeout')) {
      Notification.error({
        title: '请求超时',
        message: '请求已超时,请稍后重试。',
      })
    } else if (error.response && error.response.status === 401) {
      // 1.接口401的时候,需要调用refresh token接口
      if (localStorage.getItem('firstRefresh')) {
        // localStorage.clear()
        localStorage.setItem('TOKEN', '')
        location.reload()
      } else {
        localStorage.setItem('firstRefresh', true)
        store
          .dispatch(GET_REFRESH_TOKEN, {
            refreshToken: store.state.main.refreshToken,
          })
          .then((resp) => {
            localStorage.setItem('TOKEN')
            location.reload()
          })
          .catch((error) => {
            localStorage.setItem('TOKEN')
            location.reload()
          })
      }
    } else {
      // 处理文件流错误响应
      if (error.response && error.response.config.responseType === 'blob') {
        // 转换blob错误信息
        const reader = new FileReader();
        reader.onload = () => {
          try {
            const errorData = JSON.parse(reader.result);
            Notification.error({
              title: '导出失败',
              message: errorData.message || '文件导出失败',
            });
          } catch (e) {
            Notification.error({
              title: '导出失败',
              message: '文件导出失败',
            });
          }
        };
        reader.readAsText(error.response.data);
      } else {
        Notification.error({
          title: '请求失败',
          message: error.message,
        });
      }
    }
    logger.error(error)
    return Promise.reject(error)
  }
)

function plugin(Vue, axios) {
  if (plugin.installed) {
    return
  }
  plugin.installed = true

  Object.defineProperties(Vue.prototype, {
    $axios: {
      get() {
        return axios
      },
    },
  })
}

Vue.use(plugin, axios)
export default Vue.prototype.$axios

2、引入axios依赖

javascript 复制代码
import axios from '@/plugins/axios'

3、在api.js文件中请求接口,创建a标签实现下载导出

javascript 复制代码
// 导出
export async function exportFile(params) {
    const res = await axios.post('/api/file/export', params, {
        responseType: 'blob',
        headers: {
            'Content-Type': 'application/json'
        }
    });
    
    // 从响应头中获取文件名
    const fileName = res.headers['content-disposition']
        ? decodeURIComponent(res.headers['content-disposition'].split('filename=')[1])
        : `导出模板_${new Date().getTime()}.xlsx`;
    
    // 创建Blob对象
    const blob = new Blob([res.data], {
        type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    });
    
    // 创建下载链接
    const link = document.createElement('a');
    link.href = window.URL.createObjectURL(blob);
    link.download = fileName;
    link.click();
    window.URL.revokeObjectURL(link.href);
}          

写在最后

使用时把接口/api/file/export换为自己的接口地址即可,记住一定要加返回类型responseType: 'blob',否则可能造成文件无法正常打开。

相关推荐
疯狂的沙粒15 分钟前
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
前端·uni-app·html
小妖66619 分钟前
html 滚动条滚动过快会留下边框线
前端·html
heroboyluck33 分钟前
Svelte 核心语法详解:Vue/React 开发者如何快速上手?
前端·svelte
海的诗篇_35 分钟前
前端开发面试题总结-JavaScript篇(二)
开发语言·前端·javascript·typescript
琹箐1 小时前
ant-design4.xx实现数字输入框; 某些输入法数字需要连续输入两次才显示
前端·javascript·anti-design-vue
程序员-小李1 小时前
VuePress完美整合Toast消息提示
前端·javascript·vue.js
Uyker2 小时前
从零开始制作小程序简单概述
前端·微信小程序·小程序
EndingCoder6 小时前
React从基础入门到高级实战:React 实战项目 - 项目三:实时聊天应用
前端·react.js·架构·前端框架
阿阳微客7 小时前
Steam 搬砖项目深度拆解:从抵触到真香的转型之路
前端·笔记·学习·游戏
德育处主任Pro7 小时前
『React』Fragment的用法及简写形式
前端·javascript·react.js