[英]Get error ERR_STREAM_PREMATURE_CLOSE when attempting to stream video from Node.JS server with pipeline
我正在尝试使用 stream 库的管道 function 将视频从服务器发送到浏览器 stream 。 这是一个相当大的视频(88.7MB),这意味着我想以 5MB 的块将其 stream 到浏览器中。 我按照一些在线教程创建了服务器,但是我注意到有一些奇怪的行为,流被过早关闭(因为浏览器决定它需要在第一部分发送之前视频的最后一部分)。
这是 output:
21:01:29.215 Server is running on http://192.168.1.180:80
21:01:33.562 --------------------------------- New Request ---------------------------------
21:01:33.563 No range specified.
21:01:33.624 --------------------------------- New Request ---------------------------------
21:01:33.625 ( 1 ) Starting Pipe | Browser requested: bytes=0- | Start: 0 | End: 5242880 | Content Length: 5242881 | Content Range: bytes 0-5242880/93008043 | Video Size: 93008043
21:01:33.625 ( 1 ) Pipeline created
21:01:33.710 --------------------------------- New Request ---------------------------------
21:01:33.710 ( 2 ) Starting Pipe | Browser requested: bytes=92798976- | Start: 92798976 | End: 93008042 | Content Length: 209067 | Content Range: bytes 92798976-93008042/93008043 | Video Size: 93008043
21:01:33.711 ( 2 ) Pipeline created
21:01:33.711 ( 1 ) Pipeline error:
21:01:33.711 Error [ERR_STREAM_PREMATURE_CLOSE]: Premature close
at new NodeError (node:internal/errors:371:5)
at ServerResponse.onclose (node:internal/streams/end-of-stream:139:30)
at ServerResponse.emit (node:events:532:35)
at emitCloseNT (node:_http_server:845:10)
at Socket.onServerResponseClose (node:_http_server:233:5)
at Socket.emit (node:events:532:35)
at TCP.<anonymous> (node:net:687:12)
at TCP.callbackTrampoline (node:internal/async_hooks:130:17)
21:01:33.715 ( 2 ) Pipeline success
正如您在上面的表格中看到的那样,浏览器最初请求视频的第一个字节,但 60 毫秒后(甚至在数据发送之前)它为视频的最后部分创建了第二个管道请求(?),这使得没有感觉。 这会导致第一个管道出错,我不知道如何解决这个问题。 (发送较小的块不是一种选择)。
我注意到的另一件事是它不会尽快发送所有数据,它所做的只是等到浏览器说它需要更多数据(而不是立即发送)
当其他设备尝试同时观看视频(或从多个选项卡观看)时,我没有注意到出现此错误,所以我想知道这是否是因为它无法创建两个管道到同一个选项卡/设备/资源。
这是我的服务器代码:
const http = require("http");
const path = require("path");
const { statSync, createReadStream } = require("fs");
const { pipeline } = require("stream")
const print = require("./../../mods/print.js")
const host = "192.168.1.180";
const port = 80;
let pipe_number = 0;
let chunk_size = 1024*1024*5
let video_path = path.normalize(__dirname + "/video.mp4")
http.createServer((req, res) => {
print(`--------------------------------- New Request ---------------------------------`)
const { range } = req.headers
const { size } = statSync(video_path)
if (range) {
pipe_number++
let this_pipe = pipe_number
const parts = range.replace(/bytes=/, "").split("-")
const start = parseInt(parts[0], 10)
const end = parts[1] ? parseInt(parts[1], 10) : Math.min(start + chunk_size, size - 1)
const content_length = (end-start) + 1
const head = {
"Content-Range": `bytes ${start}-${end}/${size}`,
"Accept-Ranges": `bytes`,
"Content-Length": content_length,
"Content-Type": `video/mp4`
};
print(`( ${this_pipe} )`, `Starting Pipe`, "| Browser requested:", range, "| Start:", start, "| End:", end, "| Content Length:", content_length, "| Content Range:", `bytes ${start}-${end}/${size}`,"| Video Size:", size)
const file = createReadStream(video_path, {start, end})
res.writeHead(206, head);
print(`( ${this_pipe} )`, "Pipeline created")
pipeline(file, res, (err) => {
if (err) {
print(`( ${this_pipe} )`, "Pipeline error:", err)
} else {
print(`( ${this_pipe} )`, "Pipeline success")
}
})
} else {
print("No range specified.")
const head = {
'Content-Length': 0,
'Content-Type': 'video/mp4',
};
res.writeHead(200, head)
res.end()
}
}).listen(port, host, () => {print(`Server is running on http://${host}:${port}`)})
我从在线教程中获得了大部分内容,因为这对我来说当然是新事物。 我也没有在这里使用 express.js。
您现在可能已经发现了这个问题,但是对于未来的 googlers,我遇到了这个问题,结果发现响应中写入了不正确的内容(开始/结束字节不存在,因此正在写入整个文件)。
https://github.com/vercel/serve-handler/blob/master/src/index.js对如何正确返回 206/416 响应有很好的参考。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.