Node.js 理解节点js中的流
我遇到了一个问题,需要使用NodeJs创建和下载多个文件的zip文件。 我尝试过但失败的事情: 除此之外,现在文件也被加密了,所以我必须在动态添加到zip之前解密它们 虽然我也解决了这个问题,但当服务器在本地机器上运行时,我的解决方案工作得很好,但在Google Kubernetes引擎上失败了 经过进一步的研究,我想这可能是因为NodeJs中的流中存在背压问题,但正如文档中所述,背压是由管道方法自动处理的。 浏览器的接收速度是否可能与我的服务器/压缩的发送速度不匹配?如果是,如何解决此问题 与此问题相关的所有示例都在上面提供的链接中 除此之外,可读流还通过解密对其进行解密Node.js 理解节点js中的流,node.js,kubernetes,zipstream,archiverjs,Node.js,Kubernetes,Zipstream,Archiverjs,我遇到了一个问题,需要使用NodeJs创建和下载多个文件的zip文件。 我尝试过但失败的事情: 除此之外,现在文件也被加密了,所以我必须在动态添加到zip之前解密它们 虽然我也解决了这个问题,但当服务器在本地机器上运行时,我的解决方案工作得很好,但在Google Kubernetes引擎上失败了 经过进一步的研究,我想这可能是因为NodeJs中的流中存在背压问题,但正如文档中所述,背压是由管道方法自动处理的。 浏览器的接收速度是否可能与我的服务器/压缩的发送速度不匹配?如果是,如何解决此问
const handleEntries = ({ elem, uniqueFiles, archive, speedLimit }) => {
return new Promise((resolve, reject) => {
let fileName = elem.fileName;
const url = elem.url;
const decipher = elem.decipher;
// changing fileName if same filename is already added to zip
if (uniqueFiles[fileName] || uniqueFiles[fileName] === 0) {
uniqueFiles[fileName]++;
} else {
uniqueFiles[fileName] = 0;
}
if (uniqueFiles[fileName]) {
const lastDotIndex = fileName.lastIndexOf(".");
const name = fileName.substring(0, lastDotIndex);
const extension = fileName.substring(lastDotIndex + 1);
fileName = `${name}(${uniqueFiles[fileName]}).${extension}`;
}
let readableStream = Request(url);
// create a "Throttle" instance that reads at speedLimit bps
if (speedLimit) {
const throttle = new Throttle({ bps: Number(speedLimit) });
readableStream = readableStream.pipe(throttle);
}
// if file is encrypted, need to decrypt it before piping to zip
readableStream = decipher ? readableStream.pipe(decipher) : readableStream;
archive.append(readableStream, { name: fileName });
readableStream.on("complete", result => {
console.log("Request stream event complete : ", fileName);
resolve("done");
// readableStream.unpipe();
// readableStream.destroy();
});
readableStream
.on("error", error => {
console.log("Request stream event error fileName : ", fileName, " error : ", error);
// readableStream.unpipe();
// readableStream.destroy();
resolve("done");
})
.on("pipe", result => {
console.log("Request stream event pipe : ", fileName);
})
.on("request", result => {
console.log("Request stream event request : ", fileName);
})
.on("response", result => {
console.log("Request stream event response : ", fileName);
})
.on("socket", result => {
result.setKeepAlive(true);
console.log("Request stream event socket : ", fileName);
});
});
};
const useArchiver = async ({ resp, urls, speedLimit }) => {
resp.writeHead(200, {
"Content-Type": "application/zip",
"Content-Disposition": `attachment; filename="${outputFileName}"`,
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "GET, POST, OPTIONS"
});
const uniqueFiles = {};
const archive = Archiver("zip", { zlib: 0 });
archive.pipe(resp);
archive
.on("close", result => {
console.log("archive stream event close : ", result);
// archive.unpipe();
// archive.destroy();
})
.on("drain", result => {
console.log("archive stream event drain : ", result);
})
.on("entry", result => {
console.log("archive stream event entry : ", result.stats);
})
.on("error", error => {
console.log("archive stream event error : ", error);
reject("error");
// archive.unpipe();
// archive.destroy();
})
.on("finish", result => {
console.log("archive stream event finish : ", result);
// archive.unpipe();
// archive.destroy();
})
.on("pipe", result => {
console.log("archive stream event pipe : ");
})
.on("progress", async result => {
console.log("archive stream event progress : ", result.entries);
if (urls.length === result.entries.total && urls.length === result.entries.processed) {
await archive.finalize();
console.log("finalized : ", urls[0]);
}
})
.on("unpipe", result => {
console.log("archive stream event unpipe : ");
})
.on("warning", result => {
console.log("archive stream event warning : ", result);
});
for (const elem of urls) {
await handleEntries({ elem, uniqueFiles, archive, speedLimit });
}
};
我用archiver尝试了这段代码,在压缩大文件时获得archiver的排水事件,管道是否处理背压如果是,为什么我从archiver获得排水事件。嘿)我研究了你的代码,可以说你使用了承诺函数,你不会等到她完成。您需要使用wait new Promise()包装zipStreamer.entry。一定是这样
async function doSmth() {
const decipher = crypto.createDecipheriv(
algorithm,
Buffer.from(key),
Buffer.from(key.substring(0, 9)
));
await new Promise((resolve, reject) => {
zipStreamer.entry(readableStream.pipe(decipher), {
name: fileName
}, (error, result) => {
if (!error) {
resolve("done");
} else {
reject("error");
}
});
});
}
似乎我找到了解决方案,我对Kubernetes配置做了一些更改,即将超时时间从30秒增加到300秒,增加CPU限制,并对高达12-13 GB的文件进行了多次测试,效果非常好。
我认为将CPU从.5增加到1并增加超时时间对我来说是可行的我已经这样做了@Pavel,看看我更新的问题,也可以参考此了解有关该问题的更多信息。嗨@Pavel,谢谢你的贡献,但这个答案是错误的。眼前的问题与承诺无关。@Avius你能在google kubernetes云上与我分享你应用程序的日志吗?他们可以告诉我们问题出在哪里?是的,还是同一个问题,我尝试了很多模块,比如yazl、archiver、zip stream、zipstream,但都表现相同,在本地运行良好,但在google k8s上出现问题,很可能是反压问题。虽然管道本身可以处理背压,但不知道为什么这个问题会持续存在。在这种情况下,我看不出这个问题与您今年早些时候发布的问题(即您故意发布了一个副本)之间有任何显著差异。你也已经知道问题的答案了——操作时间太长,你的进入会切断连接。当然,这在您的本地环境中不是问题,因为您没有该设置。想确定吗?从浏览器向本地服务器发送一个配置了超时的XHR请求,然后查看发生了什么。最后,如果您使用管道和适当的转换流,则背压确实由节点处理。@Avius我也尝试过,在本地更改/配置超时不会做出任何更改,而且超时只是用于初始化响应,一旦浏览器开始接收数据,超时实际上并不重要。关于上一个问题,后来我发现这不是超时问题,即下载有时工作,有时甚至在入口超时增加后也不工作。另外,ingress并没有在固定时间(超时)后完全断开我的连接。这是随机的,文件大小和时间都不相同。当您在本地运行它时,它是直接在主机上运行,还是在docker映像内运行?