服务端返回的二进制流excel文件,前端实现下载
2026年1月13日 17:06
近期有个excel的下载功能,服务端返回的是二进制的文件流,前端实现excel文件下载。
简易axios:
// utils/request.ts
import axios, { AxiosRequestConfig, AxiosRequestHeaders } from 'axios'
axios.interceptors.request.use(config => {
……
return config
})
axios.interceptors.response.use(
response => {
return new Promise((resolve, reject) => {
……
})
}
)
export const getBlobFile = (url: string, params = {}) =>
axios({
data: params,
responseType: 'blob',
method: 'POST',
url
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}) as Promise<any>
下面是工具函数文件的方法:
// utils/index.ts
export function useState<T>(initData: T): [Ref<T>, (val?: T) => void] {
const data = ref(initData) as Ref<T>
function setData(newVal?: T) {
data.value = newVal || initData
}
return [data, setData]
}
/**
* 下载二进制文件
* @param file
* @param fn
*/
import { ref, Ref } from 'vue'
export function downloadFile(file: File, fn?: () => void) {
if ('msSaveOrOpenBlob' in navigator) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const nav = navigator as any
nav.msSaveOrOpenBlob(file, file.name)
} else {
const url = URL.createObjectURL(file)
const event = new MouseEvent('click')
const link = document.createElement('a')
link.href = url
link.download = file.name
file.type && (link.type = file.type)
link.dispatchEvent(event)
URL.revokeObjectURL(url)
fn && fn()
}
}
实现下载相关逻辑的hooks如下:
// hooks.ts
import { ref } from 'vue'
import { ElLoading } from 'element-plus'
import { EXCEL_EXPORT_URL } from '@/const/url'
import { useState, downloadFile } from '@/utils'
import { getBlobFile } from '@/utils/request'
export const OK_STATUS_CODE = '200'
export function useExportExcelFile() {
const [exportData, handleExport] = useState([] as Array<ApiRes.ExcelListItem>)
const exportLoading = ref(false)
async function exportExcelFile(params = {}) {
const text = '正在导出当前数据,请稍等~~ '
const loading = ElLoading.service({
lock: true,
text,
background: 'rgba(0, 0, 0, 0.7)',
customClass: 'export-loading-class'
})
try {
queryBlobExcelFile(params).then((res) => {
exportExcelFileByPost(res)
}).finally(() => {
loading.close()
exportLoading.value = false
})
} catch (error) {
console.error('导出失败:', error)
}
}
async function queryBlobExcelFile (params = {}) {
return new Promise((resolve, reject) => {
getBlobFile(EXCEL_EXPORT_URL, params)
.then(res => {
if (res && res?.status === OK_STATUS_CODE) {
resolve(res)
}
})
.catch(err => reject(err))
})
}
async function exportExcelFileByPost(res: {
type: string
data: Blob
}) {
const fileName = `Excel文件-${+new Date()}.xlsx`
downloadFile(new File([res.data], fileName))
}
return {
exportData,
handleExport,
exportLoading,
exportExcelFile,
}
}
在页面中的使用
</template>
<el-button
type="primary"
:disabled="false"
:export-loading="exportLoading"
@click="doExport"
>
导出
</el-button>
</template>
import { ElMessageBox } from 'element-plus'
import { useExportExcelFile } from './hooks'
// 导出
const { exportLoading, exportExcelFile } = useExportExcelFile()
function doExport() {
ElMessageBox.confirm('确定导出excel数据吗?', '导出', {
cancelButtonText: '取消',
confirmButtonText: '确认',
showClose: true
}).then(() => {
exportLoading.value = true
exportExcelFile(formData)
})
}