环境

前端是Angular 1.5.8 , nodeJs + express。Nodejs作为代理服务器,转发客户端的Get、Post请求给Tomcat,并把接受到的Tomcat请求返回给客户端。客户端指的就是浏览器。

后台是Tomcat。提供接口。

需求

后台Tomcat返回一个excel的2进制流,Nodejs接收到这个2进制流后返回给客户端。

本文的2进制流传送的是excel表格,换成其他文件也同理,反正都是2进制嘛~

问题

客户端接收到的Excel是乱码,测试了各种编码(binary、utf8、gbk等),都是乱码。

关于问题的思考

其实我饶了一圈,2进制流就是2进制流,不用考虑编码。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//Express 路由的js
router.get(/^\/(\d+)\/print/ , function(req , res , next){
var uid = req.params[0]; //正则匹配的\d
var name = req.query.name; //url传过来的excel的名字
name += '的申请表';
var options = {
hostname: global._hostname,
path: global._path+'application/'+uid+'/print',
method: 'GET',
headers: {'Cookie': 'JSESSIONID='+global.jsessionid }//请求增加JSESSIONID,后台可以凭借这个判断用户是否登录
};
var chunks = [];//返回的2进制流,需要用数组接收
var size = 0; //2进制流的size

var reqq = http.request(options, function(ress) {
ress.on('data', function (chunk) {
console.log(chunk);
chunks.push(chunk);//把二进制流存到chunk这个数组里
size += chunk.length;//size增加
});
ress.on('end', function () {
res.setHeader("Content-Type", "application/vnd.ms-excel");//把header改成xls
res.setHeader("Content-Disposition","attachment;filename="+encodeURI(name)+".xls");//把名字改成url传过来的名字,注意要用encodeURI编码一下,否则报错
res.setHeader("Expires","0");
res.setHeader("Pragma","no-cache");

var buffer = Buffer.concat(chunks , size);//调用buffer的concat连接2进制流

res.send(buffer);//返回2进制流,不用调用toString方法
});
});

reqq.on('error', function(e) {
});
reqq.end(function(){
});
});

截图

后台返回的2进制流(chunks数组里面的元素)

image
可能跟Nodejs的设计有关,接收到的2进制流不是一次接收完,一共接收了6次才接收完,我对Nodejs的原理还不是很了解,所以无法解释为什么要打印6次。

走过的弯路

之前一直以为是编码的问题,尝试的代码如下

1
2
3
4
5
6
7
//更改buffer的编码
var buf = Buffer.concat(chunks , size);
var str = iconv.decode(buf , 'utf8');//iconv是一个字符转码的工具
var str = iconv.decode(buf , 'binary');
var str = iconv.decode(buf , 'gb2312');
var str = iconv.decode(buf , 'gbk');
res.send(str);

浏览器接收到这些str后,保存成xxxx.xls,用office打开后全是乱码,因为根本就不是编码的问题嘛!

直接把后台返回的chunk返回给浏览器,尝试的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//Express 路由的js
router.get(/^\/(\d+)\/print/ , function(req , res , next){
var uid = req.params[0];
var options = {
hostname: '10.120.12.125',
path: '/mcms/v1/application/'+uid+'/print',
method: 'GET',
headers: {'Cookie': 'JSESSIONID='+global.jsessionid }
};
var results = '';

var reqq = http.request(options, function(ress) {
ress.on('data', function (chunk) {
results = results + chunk;//简单粗暴地把chunk当字符串连接到一起
});
ress.on('end', function () {
res.send(results);//把连接好的chunk字符串返回给浏览器
});
});

reqq.on('error', function(e) {
});
reqq.end(function(){
});
});

结果当然是乱码了,因为没用人家的Buffer.concat方法,字符串的+好像不能直接用在Buffer上。

iconv工具的地址(虽然没用上,但还是附一下吧)

Github

Npm

结果

浏览器成功下载了excel,并且下载的excel名字为中文的。

截个图庆祝一下

image