前端根据后端返回的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',否则可能造成文件无法正常打开。

相关推荐
橘子味的冰淇淋~1 分钟前
【解决】Vue + Vite + TS 配置路径别名成功仍爆红
前端·javascript·vue.js
利刃之灵6 分钟前
03-HTML常见元素
前端·html
kidding72313 分钟前
gitee新的仓库,Vscode创建新的分支详细步骤
前端·gitee·在仓库创建新的分支
听风吹等浪起16 分钟前
基于html实现的课题随机点名
前端·html
leluckys22 分钟前
flutter 专题 六十三 Flutter入门与实战作者:xiangzhihong8Fluter 应用调试
前端·javascript·flutter
kidding72336 分钟前
微信小程序怎么分包步骤(包括怎么主包跳转到分包)
前端·微信小程序·前端开发·分包·wx.navigateto·subpackages
微学AI1 小时前
详细介绍:MCP(大模型上下文协议)的架构与组件,以及MCP的开发实践
前端·人工智能·深度学习·架构·llm·mcp
liangshanbo12151 小时前
CSS 包含块
前端·css
Mitchell_C1 小时前
语义化 HTML (Semantic HTML)
前端·html
倒霉男孩1 小时前
CSS文本属性
前端·css