【问题标题】:How to handle client downloading gzip served from Node.js server app?如何处理从 Node.js 服务器应用程序提供的客户端下载 gzip?
【发布时间】:2019-02-16 09:15:44
【问题描述】:

我有一个关于在客户端应用程序上处理 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
}

客户端.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 以允许浏览器处理此问题。

关于我做错了什么有什么意见吗?

【问题讨论】:

  • 我发现必须设置 HTTP 标头 Content-Disposition 才能将文件作为附件处理以进行下载。 [developer.mozilla.org/en-US/docs/Web/HTTP/Headers/… 。将我的服务器标头更新为response.setHeader('Content-disposition', 'attachment; filename=',file);,将我的客户端请求更新为xhr.setRequestHeader('Content-disposition', 'attachment; filename=',file);

标签: node.js http xmlhttprequest gzip


【解决方案1】:

我做错了三件事。我设法通过创建一个新元素来获取浏览器窗口,检查该元素是否具有下载属性并将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。我想使用 node 自带的内置 zlib,但只支持单个小文件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-05
    • 1970-01-01
    • 2014-01-09
    • 1970-01-01
    • 2022-01-26
    • 2023-03-27
    • 1970-01-01
    相关资源
    最近更新 更多