我对在客户端应用程序上处理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,以允许浏览器处理此问题。
是否对我做错了什么?
答案 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,但仅支持单个小文件。