> 技术文档 > 前端解决下载的excel文件无法打开问题_vue3+ts 接口返回数据 导出无法打开文件

前端解决下载的excel文件无法打开问题_vue3+ts 接口返回数据 导出无法打开文件

【背景介绍】

项目中涉及到需要对导入的excel文件进行模板的下载,涉及到了下载的相关实现,那么我们话不多说,直接开讲:

【项目涉及到的技术栈】

Vue3+Arco-design

【开发过程中碰到的问题】

1.后端返回的数据是直接在response里面的

2.后端接收过来的数据一直显示是string类型,但转换成blob下载下来的文件又打不开

3.后端在postman中可以直接下载到文件,并且能够正常的打开;前端通过接口获取到的数据,在浏览器下载下来的文件大小与实际的文件大小不符,且无法正常的打开文件

总结下来,核心的问题就是打不开!打不开!打不开!!!!!!

 【关于产生原因的思考】

在浏览器、豆包、deepseek中查找了很多相关的帖子,总结下来基本就是:

1.要确认请求的接口对不对(都能下载到文件,证明接口肯定是对的啊)

2.要设置responseType;(可以很确定的说,我已经在请求的时候加上了)

3.要设置Content-Type,值要根据要下载的文档类型需要,选择对应的值

例如:vnd.openxmlformats-officedocument.spreadsheetml.sheet excel表格使用的类型

4.一堆杂七杂八的属性(防止xss攻击的啊、限制必须下载设置的type类型的啊等等,反正没有一个解决了我的问题)

后来又自己思考了一下可能造成这样情况的原因:

1.按照网上描述,接口返回数据不应该直接在response里面,而是在response.data里面,是否是拦截器有问题

2.接到数据之后对数据进行了转换,是否是在转换的途中出现了问题

【最终如何解决的】

在cursor中,让cursor解决了,并让它封装了一个下载的函数,可以看到有相应的注释

import { Message } from \'@arco-design/web-vue\';import { getToken } from \'@/utils/auth\';/** * 通用文件下载工具 * * 使用fetch API下载文件,支持各种文件类型 * 自动处理身份验证、错误处理和资源清理 *//** * 下载配置接口 */interface DownloadConfig { /** 下载地址 */ url: string; /** 文件名(包含扩展名) */ filename: string; /** 请求方法,默认GET */ method?: \'GET\' | \'POST\'; /** 额外的请求头 */ headers?: Record; /** 请求体数据(POST请求时使用) */ data?: any; /** 是否显示成功提示,默认true */ showSuccessMessage?: boolean; /** 是否显示错误提示,默认true */ showErrorMessage?: boolean;}/** * 使用fetch下载文件 * * @param config 下载配置 * @returns Promise */export async function downloadFile(config: DownloadConfig): Promise { const { url, filename, method = \'GET\', headers = {}, data, showSuccessMessage = true, showErrorMessage = true } = config; try { console.log(`开始下载文件: ${filename}`); // 1. 获取API基础地址和用户token const baseURL = import.meta.env.VITE_API_URL; const token = getToken(); // 2. 准备请求配置 const requestConfig: RequestInit = { method, headers: { // 默认请求头 \'Authorization\': `Bearer ${token}`, \'RSC\': import.meta.env.VITE_CLIENT_RSC || \'\', // 用户自定义请求头 ...headers } }; // 3. 如果是POST请求且有数据,添加请求体 if (method === \'POST\' && data) { requestConfig.body = JSON.stringify(data); requestConfig.headers = { ...requestConfig.headers, \'Content-Type\': \'application/json\' }; } // 4. 发起下载请求 const fullUrl = url.startsWith(\'http\') ? url : `${baseURL}${url}`; const response = await fetch(fullUrl, requestConfig); // 5. 检查响应状态 if (!response.ok) { throw new Error(`下载失败: HTTP ${response.status} ${response.statusText}`); } // 6. 将响应转换为Blob const blob = await response.blob(); // 7. 验证文件数据 if (blob.size === 0) { throw new Error(\'下载的文件为空\'); } console.log(`文件下载完成:`, { 文件名: filename, 文件大小: `${(blob.size / 1024).toFixed(2)} KB`, 文件类型: blob.type || \'未知类型\' }); // 8. 创建下载链接并触发下载 const downloadUrl = window.URL.createObjectURL(blob); const link = document.createElement(\'a\'); link.href = downloadUrl; link.download = filename; link.style.display = \'none\'; // 添加到页面,点击下载,然后移除 document.body.appendChild(link); link.click(); document.body.removeChild(link); // 9. 清理临时URL,防止内存泄漏 window.URL.revokeObjectURL(downloadUrl); // 10. 显示成功提示 if (showSuccessMessage) { Message.success(`${filename} 下载成功`); } } catch (error: any) { // 错误处理 console.error(\'文件下载失败:\', error); if (showErrorMessage) { const errorMessage = error?.message || \'未知错误\'; Message.error(`下载失败: ${errorMessage}`); } // 重新抛出错误,让调用方可以进一步处理 throw error; }}/** * 下载Excel文件的便捷方法 * * @param url 下载地址 * @param filename 文件名 * @param data 请求数据(可选) */export async function downloadExcel( url: string, filename: string, data?: any): Promise { return downloadFile({ url, filename, method: data ? \'POST\' : \'GET\', data, headers: { \'Accept\': \'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\' } });}/** * 下载PDF文件的便捷方法 */export async function downloadPDF( url: string, filename: string, data?: any): Promise { return downloadFile({ url, filename, method: data ? \'POST\' : \'GET\', data, headers: { \'Accept\': \'application/pdf\' } });} 
/** * 下载测试报告 - 使用GET请求 * * @param params 下载参数 * @returns Promise */export const downloadReport = async (params) => { const { downloadExcel } = await import(\'@/utils/download\'); // 构建GET请求的URL参数 const searchParams = new URLSearchParams({ 要传入的参数 }); return downloadExcel( `后端接口的url`, `文件名称.xlsx`, );};

【鉴于有小伙伴不知道如何调用这个定义的接口,现补充调用接口相关的板块代码】

/** * 下载报告 - 使用GET请求 * * @param record 任务记录数据 */const handleDownload = async (record) => { try { // 调用API下载报告(GET请求) await downloadReport({ 要传入到接口的参数 }); } catch (error) { console.error(\'下载测试报告失败:\', error); message.error(\'下载测试报告失败\'); }};

总结:

以上就是我对该问题的解决,就我目前的理解来讲, 我认为应该是我之前传入给window.URL.createObjectURL函数的参数不对,之前传入的参数是blob开头,而正常传入的应该是携带有项目请求转接使用的请求头才对,写这篇文章的目的主要就是为了记录碰到的下载问题,以便后续如果碰到类似问题,可以解决,欢迎来评论探讨呀