前端导出文件流方法

404 阅读2分钟

项目背景:vue-cli3.0项目中已经进行二次接口封装,调用导出文件流接口

方法一

运用此方法,导出文件的名称由后端统一命名,前端无法自定义导出文件名称

// 定义 downloadApi.js
import request from '../request'
import axios from 'axios'

const doExport = (url, params = {}) => {
  console.log(axios.create().baseURL)
  let downloadUrl = 'api' + url
  var xhr = new XMLHttpRequest()
  xhr.open('POST', downloadUrl, true)
  xhr.setRequestHeader(
    '_token', sessionStorage.getItem('token')
  )
  xhr.setRequestHeader(
    'Content-Type', 'application/json;charset=UTF-8'
  )
  xhr.responseType = 'blob'
  xhr.onload = function () {
    if (this.status === 200) {
      var blob = this.response
      if (navigator.msSaveBlob == null) {
        var a = document.createElement('a')
        var headerName = xhr.getResponseHeader('Content-disposition')
        var fileName = decodeURIComponent(headerName).substring(20)
        a.download = fileName
        a.href = URL.createObjectURL(blob)
        document.querySelector('body').append(a) // 修复firefox中无法触发click
        a.click()
        URL.revokeObjectURL(a.href)
        a.remove()
      } else {
        navigator.msSaveBlob(blob, fileName)
      }
    }
  }
  xhr.send(JSON.stringify(params))
}

export default {
  doExport
}

在文件中引用

// index.vue
import downloadApi from '@/utils/downloadApi'

doExport () {
  downloadApi.doExport('/yourself url', `yourself params`)
}

方法二

运用此方法,前端可以自定义导出文件名称

// 定义 downloadApi.js
import request from '../request'
import axios from 'axios'
const exportTask = (url, data, name) => request({
  url,
  data,
  responseType: 'blob',
  method: 'POST'
}).then(res => {
  download({
    data: res,
    name
  })
})

const download = options => {
  console.log(options)
  let blob = new Blob([options.data], { type: options.type || 'application/vnd.ms-excel' })
  if ('download' in document.createElement('a')) {
    let downloadElement = document.createElement('a')
    let href = window.URL.createObjectURL(blob)
    downloadElement.href = href
    downloadElement.download = options.name
    document.body.appendChild(downloadElement)
    downloadElement.click()
    document.body.removeChild(downloadElement)
    window.URL.revokeObjectURL(href)
  } else {
    window.navigator.msSaveBlob(blob, options.name)
  }
}

export default {
  exportTask
}

在文件中引用

// index.vue 
import downloadApi from '@/utils/downloadApi'

doExport () {
  downloadApi.exportTask('/yourself url', `yourself params`, 'custom_export_name')
}

小结

1- 以上两种方法都是post请求,如果接口是get请求,对应修改即可;也可以把这两种方法合并到一起并export,方便调用和查看。
2- 如果为了方便接口统一管理而不想在.vue文件中使用url,可以参考使用下面的调用方式(以方法二为例)

// 定义 /utils/downLoad.js
export const download = options => {
  let blob = new Blob([options.data], { type: options.type || 'application/vnd.ms-excel' })
  if ('download' in document.createElement('a')) {
    let downloadElement = document.createElement('a')
    let href = window.URL.createObjectURL(blob)
    downloadElement.href = href
    downloadElement.download = options.name
    document.body.appendChild(downloadElement)
    downloadElement.click()
    document.body.removeChild(downloadElement)
    window.URL.revokeObjectURL(href)
  } else {
    window.navigator.msSaveBlob(blob, options.name)
  }
}

// 定义 /api/myApi
const exportTemplate = params => request({
  url: 'yourself url',
  params,    
  responseType: 'blob',
  method: 'GET'
})
export default {
  exportTemplate
}

// 引用
import { download } from '@/utils/downLoad'
import myApi from '@/api/myApi'

doExport (item) {
  myApi.exportTemplate({ id: item.rowId }).then((res) => {
    download({ data: res, name: item.templateName + '列表' })
  })
}

遇到的问题及解决方案

问题一 vue element admin 后台请求Uncaught (in promise) Error: Error at webpack_exports.def 问题

image.png

一般出现这种问题的原因是在代码中写入了请求拦截,限制了响应返回值条件导致出错,检查修改即可。

image.png

问题二 通过后台返回下载文件到本地打开显示乱码

在封装的请求方法里添加

responseType: 'blob'    // 或者 'arraybuffer'