如何处理从Node.js服务器应用提供的客户端下载gzip?

时间:2018-09-11 19:43:15

标签: node.js http xmlhttprequest gzip

我对在客户端应用程序上处理gzip响应有疑问。我希望客户端的浏览器弹出一个警报“您要如何处理?”下载提示。

我的Node.js服务器正在将我的文件压缩为gzip格式,然后通过HTTP写响应发送。我的客户端收到HTTP 200状态,尽管响应的大小比我的文件小,并且没有任何内容填充我的Web应用程序。我期望浏览器能够处理对发送gzip的服务器的这种响应。类似于gmail处理文件下载的方式。你能帮我看看我是否错过了什么吗?

server.js

var server = http.createServer(function(request, response) {
    if (request.url === '/download'){
        let data_zip = retrievedata()
        const scopedata_zip = ('./scopedata.txt.gz')
        response.writeHead(200, { 'Content-Encoding': 'gzip' });
        response.writeHead(200, { 'Content-Type': 'application/javascript' });
        response.write(scopedata_zip);
    }
})           

var retrievedata = () =>{
    const gzip = zlib.createGzip();
    const inp = fs.createReadStream('scopedata.txt');
    const out = fs.createWriteStream('scopedata.txt.gz');
    inp.pipe(gzip).pipe(out);
    return out
}

Client.js

var downloadData=()=>{
var xhr = new XMLHttpRequest();
xhr.open('POST', 'download', true);
//xhr.setRequestHeader("Accept-Encoding", "gzip")
xhr.setRequestHeader("Encoding", "null")
xhr.onload = function (){
    if(this.status == 200){
        let form = document.createElement("form");
        let element1 = document.createElement("input"); 
        document.body.appendChild(form);
        let response = this.responseText
        console.log(response)
        document.getElementById("state").innerHTML = 'download'
        document.getElementById("index").innerHTML = response;
        // document.getElementById("state").appendChild(form)
    }
}

xhr.onerror = function(err){
    console.log("request error...",err)
}

xhr.send()

}

客户端只是将我的索引div填充到响应中,但是什么也没收到。

我的gzip文件是327mb。 Chrome检查器网络说,此请求只有170B,因此我没有收到文件。

注释xhr.setRequestHeader("Accept-Encoding", "gzip")被注释掉,因为出现此错误:拒绝设置不安全的标头“ Accept-Encoding”。我已将其设置为null,以允许浏览器处理此问题。

是否对我做错了什么?

1 个答案:

答案 0 :(得分:0)

我做错了三件事。通过创建一个新元素,检查该元素是否具有下载属性,并将XHR.Response附加为href的位置,我设法获得了浏览器窗口。我问题的第二部分没有收到带有适当请求标头的zip文件。由于我的zip文件较大,因此浏览器将二进制缓冲区流作为blob处理。阅读有关XHR响应类型XHR.response的更多信息。另一个问题是在服务器端,它使用fs.readFile来读取zip作为缓冲区。由于我的zip文件由多个文件fs.readFile组成,因此它将在第一个文件的末尾停止读取。

所以我的客户代码看起来像

var xhr = new XMLHttpRequest();
document.getElementById("state").innerHTML = ' '
document.getElementById("index").innerHTML = ' ';
xhr.open('POST', 'download', true);
xhr.setRequestHeader('Content-disposition', 'attachment')
xhr.setRequestHeader("Content-type","application/zip"); //content-type must be set
xhr.setRequestHeader("Encoding", "null") //unsure of why I need this but it doesnt work with out it for me
xhr.responseType = "blob"; // This must be set otherwise the browser was interpretting the buffer stream as string instead of binary

xhr.onload = function (){
    if(this.status == 200){
        let form = document.createElement("form");
        let element1 = document.createElement("input"); 
        document.body.appendChild(form);

        let response = this.response // defined as blob above
        document.getElementById("state").innerHTML = 'download'
        document.getElementById("index").innerHTML = response;
        var blob = new Blob([response], {type: "application/zip"});
        var file = URL.createObjectURL(blob);
        filename = 'Data.zip'
        var a = document.createElement("a");
        if ("download" in a) { //check if element can download
          a.href = file;
          a.download = filename;
          document.body.appendChild(a);
          a.click(); //automatically browser download
          document.body.removeChild(a);
    }
}

服务器端

else if (request.url === '/download'){
                    archiveZip((data)=>{ // using archivezip and adding a callback function to insert my routes XHR response
                        response.setHeader('Content-Type', 'application/zip')
                        response.setHeader('Content-Length', data.length) // this is important header because without it the browser might truncate the entire response especially if there are end of file characters zipped up in the buffer stream
                        response.setHeader('Content-disposition', 'attachment; filename="Data.zip"');
                        response.end(data);
                    })

                }


var archiveZip = (callback) =>{
var output = fs.createWriteStream(__dirname + '/Data.zip'); //output
var archive = archiver('zip', {zlib: { level: 9 }});

output.on('close', function() {
  console.log(archive.pointer() + ' total bytes');
  console.log('archiver has been finalized and the output file descriptor has closed.');
  fs.readFile('./Data.zip', function (err, content) {
                        if (err) {
                            response.writeHead(400, {'Content-type':'text/html'})
                            console.log(err);
                            response.end("No such file");    
                        } else {
                            callback(content);
                        }
                    });
});

output.on('end', function() {
  console.log('Data has been drained');
});

archive.on('error', function(err) {
  throw err;
});

archive.pipe(output);

// append a file
archive.file(data_files + '/parsed_scope.json', { name: 'parsed_scope.json' });
archive.file(data_files + '/scopedata_index.json', { name: 'scopedata_index.json' });
archive.file(data_files + '/scopedata.txt', { name: 'scopedata.txt' });
archive.finalize();

我正在查看许多zip库,它们可以处理包含多个文件的目录的压缩,并且与archiver一起使用。我想使用节点随附的内置zlib,但仅支持单个小文件。