我需要在 Node.JS 中实现一个 HTTP 服务器,使用内置的
http
模块,该模块将从文件系统提供一个大文件。但是,我遇到了一个问题,我的解决方案仅在直接使用时才有效。当它放在 Nginx 反向代理后面时它不起作用。
我当前的实现(示例):
let http = require('http');
let fs = require('fs');
http.createServer(function (req, res) {
let size;
size = fs.statSync("./some/file").size;
res.setHeader("Content-Type","application/octet-stream");
res.setHeader("Content-Length", `${size}`);
res.setHeader("Content-Disposition", "attachment; filename='some.filename'");
res.flushHeaders();
let seek = 0;
let buf;
let bs = 4096;
let fd;
fd = fs.openSync("./some/file", "r");
while (seek < size) {
buf = Buffer.alloc(bs);
fs.readSync(fd,buf,null,bs,seek);
res.write(buf);
seek = seek + bs
}
res.end();
}).listen(8080);
本地使用效果很好。但它在反向代理后面不起作用。浏览器显示错误,在
curl
中我看到:
HTTP/2 stream 1 was not closed cleanly: PROTOCOL_ERROR (err 1)
我使用 nginx 作为反向代理,并且它被配置为在与上游通信时仅使用 http1.1。 如果我这样实现就没有问题:
...
res.end(fs.readFileSync("./some/file"));
...
但是对于几百GB那么大的文件显然是不可能的。 这里不存在XY问题。我知道有一些用于提供文件的工具。我知道nginx可以做到这一点。 但我有兴趣修复我的实现。 由于它只有在反向代理后面时才无法工作,我认为它在某种程度上不符合 HTTP 协议。但我自己却无法弄清楚。 谢谢!
我希望文件传输能够直接和在 Nginx 后面正常工作。
这里的问题很明显,但我花了一些时间才意识到。
身体长度与标题所宣传的不符
Content-Length
标题
当我们
read
进入Content-Length
。因为,考虑到任何文件都被填充为 一些 HTTP 实现至少具有最低程度的兼容性,以应对轻微违反 HTTP 标准的情况。但是发送比
Content-Length
中设置的更多/更少的字节就太多了。当直接使用浏览器时,某些浏览器无论如何都会尝试显示响应,尽管长度无效(甚至更糟糕的情况)。但是当使用 Nginx 或其他反向代理时,此类响应导致错误是绝对正确的行为。