Node / Express - POST请求从远程服务器接收zip,传回客户端

时间:2016-11-15 10:32:14

标签: angularjs node.js express post

所以这里是我的设置的架构概述:

  • PEBKAC
  • AngularJS Frontend
  • NodeJS / Express Middleman
  • Spring / Hibernate API
  • MySQL数据库

现在在Spring API中,我有一个API调用来动态生成一个zip文件。此调用返回一个分块的二进制流。

我需要做的是在Node / Express中捕获此流并将其传递回AngularJS前端。

用户旅程是用户检查要导出的项目列表,单击按钮以确认导出,并向节点发出http请求。 Node然后向Spring发送请求,Spring返回流,并将结果发送回AngularJs。

不需要任何关于我们应该如何使用Node / Express或Spring的讲座 - 架构将会发生变化,现在这是很多遗留代码。

对Spring的当前快递请求:

exports.zipRequest = function(path, postBody, onResult) {

    var options = {
        host: host,
        port: port,
        path: springContext + path,
        method: 'POST',
        encoding: 'binary',
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/zip'
        }
    };

    var req = http.request(options, function (res){

        var output = '';
        res.setEncoding('binary');

        res.on('data', function (chunk) {
            output += chunk;
        });

        res.on('end', function () {
            onResult(res.statusCode, output);
        });


    }).on('error', function(e) {
        console.log("Got error: " + e.message);
        req.end();
    });

    req.write(postBody.toString());
    req.end();
};

当前节点API:

exports.exportSessions = function(req, res) {

    var dataset = req.user.dataset;
    var id = req.params.id;

    var arr = JSON.stringify(req.body);

    var today = new Date();
    var filename = today.toISOString();
    filename = filename.replaceAll("-","_");
    filename = filename.replaceAll(":","_");
    filename = filename.replace(".","_");

    var path = '/Export/dataset/'+dataset+'/exportZip/';

    api.zipRequest(path, arr, function(statusCode, result) {

        if(statusCode != 200) {

            console.log(statusCode);
            console.log(result);
            res.send(statusCode, "Problem from backend API");

        } else {

            if (result != null || typeof result != 'undefined') {

                res.type('application/zip')
                res.attachment(filename+'.zip');
                res.send(result, 'binary');

            } else {

                res.send(statusCode, "Undefined Result" + result);

            }
        }
    });

};

想要弄清楚如何让客户端浏览器保存返回的文件 - 因为我可以看到二进制字符串很好,并且我设置了所有必需的标头。我已经尝试了从在Express中创建缓冲区并将其返回到Node API响应中的res.write()并且似​​乎没有任何工作。

编辑:

所以我设法使用以下内容将Express拉回到客户端:

    var req = http.request(options, function (res){

        var today = new Date();
        var filename = today.toISOString();
        filename = filename.replaceAll("-","_");
        filename = filename.replaceAll(":","_");
        filename = filename.replace(".","_");

        res.setEncoding('binary');

        res.pipe(onResult);
};

并在Node中:

exports.exportSessions = function(req, res) {

    var dataset = req.user.dataset;
    var id = req.params.id;

    var arr = JSON.stringify(req.body);

    var today = new Date();
    var filename = today.toISOString();
    filename = filename.replaceAll("-","_");
    filename = filename.replaceAll(":","_");
    filename = filename.replace(".","_");

    var path = '/Export/dataset/'+dataset+'/exportZip/';
    res.type('application/zip')
    res.attachment(filename+'.zip');
    return api.zipRequest(path, arr, res);
}

我可以在开发者控制台中看到整个文件流到客户端(浏览器),响应头如下:

access-control-allow-headers:Origin, Content-Type, X-Auth-Token
access-control-allow-methods:POST, GET
access-control-allow-origin:*
cache-control:private, no-cache, must-revalidate
Connection:keep-alive
content-disposition:attachment; filename="2016_11_16T09_34_50_976Z.zip"
content-type:application/zip
Date:Wed, 16 Nov 2016 09:34:51 GMT
expires:-1
Transfer-Encoding:chunked
X-Powered-By:Express

虽然仍然没有骰子获得保存提示。

1 个答案:

答案 0 :(得分:1)

所以我终于明白了。希望这给任何想要保留初始请求的人提供了一个相当深入的答案,并从次要中传回响应。

我通过使用npm request模块解决了这个问题,并将我的Node API调用从POST修改为GET:

var request = require('request');

exports.exportSessions = function(req, res, next) {

    var dataset = req.user.dataset;
    var arr = decodeURIComponent(req.query.ids);

    var path = '/Export/dataset/'+dataset+'/exportZip/';
    console.log('Exporting Data '+arr);

    var options = {
        method: 'POST',
        uri: 'http://' + api.host + ':' + api.port + api.springContext + path,
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'application/zip'
        },
        json: true,
        body: JSON.parse(arr)
    };

    request.post(options).pipe(res)
}

并将我的Angular请求更改为只使用获取URL打开一个新窗口:

$scope.exportData = function() {
    var ids = $scope.sessions.selected.map(function(ses){
        return ses.id;
    });
    var pars = encodeURIComponent(JSON.stringify(ids));
    window.open('/api/sessions/export/sessions?ids='+pars+'&access_token='+Auth.getToken(), '_blank');
}

这将使用NodeJS API路径简短地打开一个新窗口,然后调用Spring / Hibernate API并将响应作为对原始请求的响应进行管道传输。

Node API的最后一行使用request向Spring / Hibernate服务器发出POST请求,并通过初始res对象传回响应。希望这有助于其他任何想要将远程二进制文件传递回客户端的人。