需求
在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',否则可能造成文件无法正常打开。