后端返回文件流,前端导出文件损坏怎么办(踩坑记录)

您所在的位置:网站首页 剪映导出的文件格式已损坏怎么解决 后端返回文件流,前端导出文件损坏怎么办(踩坑记录)

后端返回文件流,前端导出文件损坏怎么办(踩坑记录)

2024-06-30 21:45| 来源: 网络整理| 查看: 265

后端返回文件流,前端如何接收处理

今天来和大家分享一下我是如何遇到这个问题的

我在做我公司项目的一个excel导出的功能,项目的后端我使用的是nodejs的egg框架

简单介绍一下我这个项目的一些插件吧,首先这个项目集成了egg的swagger,使用了egg-sequelize,数据库的orm框架,以及跨域中间件等等。

我在做用户管理的时候,想给它加一个导出的功能,我在网上搜索找到了一个处理excel的库,node-xlsx,这是一个专门用来处理excel文件的工具库,也是chatgpt推荐使用的一个工具库。

话不多说,直接看代码!!!

router路由

router.post('/auth/admin/export', controller.auth.admin.export); //导出用户

对应的controller

async export() { const { ctx,service } = this; const buffer = await service.auth.admin.exportAdmin(ctx.request.body); // 设置响应头,指定文件名和文件类型 ctx.set('Content-Type', 'application/octet-stream'); ctx.set('Content-Disposition', 'attachment; filename=admin.xlsx'); // 发送导出的文件给前端 ctx.body = buffer; }

对应的service

async exportAdmin(options) { const exportData = await this.getExportData('Auth.Admin', options); const exportTheadArr = ['邮箱', '姓名', '电话', '创建时间', '更新时间']; const exportTheadKeyArr = ['email', 'name', 'phone', 'create_time', 'update_time']; const uExportData = this.getUExportData({ exportTheadArr, exportTheadKeyArr, exportData }); const buffer = await this.service.export.exportData(uExportData); return buffer; }

我这边有一个service专门负责处理导出的

const xlsx = require('node-xlsx'); //... async exportData(data) { // 通过 node-xlsx 生成 Excel 数据 const excelData = [ { name: 'Sheet1', data: data }, // data 是一个二维数组,表示 Excel 中的数据 ]; const buffer = xlsx.build(excelData); return buffer; }

目前的导出很简单,只有这些代码,然后在我的apifox里面测试也是正常的,导出来的效果是这样的

在这里插入图片描述

然后在前端去接收这个blob,去下载的时候,我的拦截器是这么写的

axios.interceptors.response(response=>{ if(response.data.type === 'application/octet-stream'){ const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet; charset=utf-8' }) // 获取文件名,根据自己需要的分割 const fileName = response.headers['content-disposition'].split(';')[1].split("=")[1] const a = document.createElement('a') const url = window.URL.createObjectURL(blob) a.href = url a.download = fileName document.body.appendChild(a) a.style.display = 'none' a.click() document.body.removeChild(a) window.URL.revokeObjectURL(url) } //... })

我感觉我的写法没有问题,也能正常导出但是发现导出的文件一直损坏。我去百度搜了几篇博客,它们都说是需要在axios里面添加responseType为blob才行

后面我调整了一下,发现还是不行,文件还是损坏,我也试过改成arraybuffer,甚至换成get请求试了下,很多方式我都尝试了一遍

export async function exportAdmin(exportInfo) { const resp = await request({ url: '/auth/admin/export', method: 'post', responseType: 'blob', data: { //... } }); return resp.data; }

最后我怀疑是不是axios的版本存在问题,我还专门去下了其他的版本,发现还是存在问题,我又开始想会不会是后端返回的数据流有问题,因为文件导出这一块之前我没试过,我测试了下apifox的导出和swagger的导出,内容都能正常显示,我直接给整不会了。

我也一直尝试着定位问题,我发现拦截器里面打印的response.data一直是一个给转码的string字符串,我就觉得纳闷了,是axios拦截器的问题还是axios没有正确的接收到后端返回的二进制流,我甚至一度怀疑是我后端的问题。

直到后面找了一个大佬帮忙,他给我提供了一个导出文件的接口,但是我测试了发现还是存在问题,我已经明确的知道就是我前端的问题,但是我分析不出来是什么问题,这个项目是基于antd vue admin的。

我索性直接用vue脚手架重新创建了一个空项目来测试项目,然后使用了大佬的接口再进行了一下测试,结果竟然可以了,看到了response.data打印结果出来,真的很激动,搞了快两天的问题

在这里插入图片描述

找到大致的方向我就开始看我项目的一个全局引用,我在想是不是引入了哪些插件或者工具把请求拦截做处理才会让response.data的值是一个字符串。

果然让我找到了,这万恶的根源

在这里插入图片描述

我发现这里引入了一个mockjs,mockjs是前端用来模拟请求的,它会对请求做一个拦截,而且这里我测试了一下,单纯的引入mockjs不会出问题,但是只要引入了mockjs,并且写了Mock.mock去模拟一个请求拦截,那么所有二进制数据都会被转成字符串类型,所以在平时开发的过程中,mockjs还是慎重使用。如果是单纯前端所有接口都是mock可以使用mock数据,但是如果有真实接口还是要把mock注释掉,防止mock影响我们的axios的一个响应结果。

这个问题还是花了我两天才解决,虽然不是什么很难的事情,但是这一个过程中还是学到了很多东西,也有了启发,一个问题的出现就会带动我全身的一个细胞,不停为了解决问题而分析,搞技术就是这样,遇到问题一定要学会去分析问题,定位到具体的问题,然后再解决它,最后总结一下,为什么会遇到这种问题,这种问题遇到之后的一个解决思路是什么,后面如何规避在出现这种问题。

哈哈哈,希望我这篇博客能帮助到有缘人

记录时间:2023.9.25 11:27



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3