因公司架构原因前端请求需要经过NodeJs进行接口转发再返回到浏览器,在对导出Excel接口进行代理请求时发现导出到浏览器的数据出现乱码的情况,一顿操作最后发现是编码的问题。
即便java后端已经对数据做了处理,经node接口请求响应之后文件流的编码还是被修改了,之前一直想着应该以一对一的请求响应处理方式去获取最原始的数据的心理设定作祟,不应对响应response做处理,而是应该直接拿到什么返回什么。一直没往编码问题方向去思考,一番折腾甚至使用了三种不同的请求方式还是返回乱码数据,不禁开始怀疑自己这几年前端是不是白做了,还适合做开发吗??是不是该转行去送外卖了...
排错思路:
-
使用postman直接请求后端接口,不经过nodeJs转发。复制postman请求后生成的curl命令代码,打开命令行工具进行粘贴,并在最末尾添加
-o test_java.xls后回车,将请求得到的文件流保存为Excel后打开查看的方式看看是不是java后端的接口本身就有问题,我这里打开后发现数据工工整整排列整齐... -
打开浏览器控制台并切换到network模块下,右键移动到Copy选项,点击
Copy as cURL,仿造上一步在命令最末尾追加-o test_nodeJs.xls打开文件查看是否出现乱码,我在这一步发现打开后的文件全是乱码...问题就出在NodeJs对后端接口的请求上 -
假设上面两步打开之后发现文件均没有问题,那么就是前端客户端对文件流的处理代码逻辑上有问题
以下是我的客户端代码,没有问题...
客户端代码
创建a标签并模拟点击事件下载Excel文件
if (isBlob(res.data)) {
const filename = decodeURIComponent(
headers['content-disposition'].match(/(filename=(.*))/)[2]
); // 服务端需要在响应头设置 Content-Disposition
const blob = new Blob([res.data], {
type: 'application/octet-stream',
});
const linkNode = document.createElement('a');
linkNode.download = filename;
linkNode.style.display = 'none';
linkNode.href = URL.createObjectURL(blob);
document.body.appendChild(linkNode);
linkNode.click();
URL.revokeObjectURL(linkNode.href);
document.body.removeChild(linkNode);
} else {
// 导出失败
}
NodeJs端代码
nodeJs采用的是egg框架,以下三种请求方式在前期没有没有设置编码的时候均是返回乱码。后续在发现问题点之后经过修改都能成功获取到一个Buffer缓存,直接往浏览器端返回即可。我采用的是axios的方案,无他,就是对这个库比较熟而已,在不同环境下可能会出现请求响应不一致的情况,这里将三种请求都分享给大家
const axios = require('axios');
const rp = require('request-promise');
/**
* 方案1 egg内置的curl请求
* 关键配置:不设置dataType属性
* dataType的作用是明确告诉 HttpClient 以 xml等文本 格式处理返回的响应 body,设置为‘json’ ,以 JSON 格
* 式处理返回的响应 body
* 不设置dataType:默认 HttpClient 不会做任何处理,会直接返回 Buffer 类型数据
*/
const result = await ctx.curl(requestURI, {
method: 'POST',
timeout: 60000,
data: JSON.stringify(body),
});
ctx.set({
'Access-Control-Expose-Headers': 'Content-Disposition',
'content-disposition': result.headers['content-disposition'],
});
ctx.body = result.data;
/**
* 方案2 axios
* 关键配置: responseType: 'arraybuffer', responseEncoding: 'binary'
*/
const result = await axios({
method: 'POST',
url,
params: queryParams,
data: body,
responseType: 'arraybuffer',
responseEncoding: 'binary',
});
ctx.set({
'Access-Control-Expose-Headers': 'Content-Disposition',
'content-disposition': result.headers['content-disposition'],
});
ctx.body = result.data;
/**
* 方案3 request-promise
* 关键配置: encoding: null,
* 设置encoding为binary还是会乱码,后续发现在stack overflow上发现设置为null能解决问题
* address:https://stackoverflow.com/questions/48752822/request-promise-download-pdf-file/48753392#48753392
*/
const result = await rp({
method: 'POST',
uri: requestURI,
body: JSON.stringify(body),
resolveWithFullResponse: true,
encoding: null,
});
ctx.set({
'Access-Control-Expose-Headers': 'Content-Disposition',
'content-disposition': result.headers['content-disposition'],
});
ctx.body = result.body;
总结
- 前期一直给自己设定了不要对后端数据做二次处理的思维方式限制了自己的发挥,在出现乱码之后第一时间想到的是可能请求库对数据做了些处理。换一个试试呢?无奈还是乱码...
- 对于http协议,请求头 & 响应头的不了解导致在请求出现问题时的思考范围,在请教了公司其他同事之后其实也听得一脸懵圈,只是为了需要写而写,不知道为什么这样写。知其然而不知其所以然
- 处理数据流还是较少出现的情况,前端在处理不同场景下的响应体这方面的知识也还需要去补充完整
谨以此文,纪念此次开发导出Excel功能中遇到的问题
路漫漫其修远兮,吾将上下而求索