前端根据后端返回的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 小时前
设计稿是 #4A7C6F,页面量出来是 #4B7C6F:HEX、HSL、透明度、canvas 来回转的七个坑
前端·javascript·css·canvas
郑州光合科技余经理2 小时前
海外版外卖加盟:总站与分站配送规则怎么分开管
java·开发语言·前端·后端·uni-app·php·ai编程
小呆呆6663 小时前
副业搞起来,小说,漫画,漫剧的成本优化思路
前端·后端·面试
Dovis(誓平步青云)3 小时前
浇水提醒刚弹出又消失,植物状态别只存一个百分比
开发语言·前端·javascript·pdf·ecmascript·电脑
码艺-Alimjan4 小时前
Vben Admin 新增维吾尔语 Vben-Modal的关键坑之一
前端·javascript·vue.js
可乐鸡翅yeah_4 小时前
hls.js 手动自定义 http 请求 loader,修改请求头实战
开发语言·前端·javascript·网络协议·http·ecmascript·m3u8在线
IT_陈寒4 小时前
Vite静态资源导入这个坑我帮你们踩过了
前端·人工智能·后端
广州华水科技5 小时前
大坝安全监测解决方案:单北斗GNSS形变监测系统应用与维护
前端
yivifu5 小时前
中文古籍电子书注释集成
前端·javascript·python·beautifulsoup·epub
默_笙6 小时前
🛴 从散件到整机:DeepAgents 与 Agent 身上预留的那些"插槽"(前置介绍)
前端·javascript