我试图使用流库的管道功能将视频从服务器流流到浏览器。这是一个相当大的视频(88.7MB),这意味着我想将它以5MB块的形式流到浏览器。我根据一些在线教程创建了服务器,但是我注意到有一些奇怪的行为,流被过早关闭(因为浏览器决定在发送第一部分之前就想要视频的最后一部分)。
这是输出:
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 As之后(甚至在数据被发送之前),它会为视频(?)的最后部分创建第二个管道请求。这毫无意义。这会导致第一个管道出错,我不知道如何修复这个问题。(发送较小的块不是一个选项)。
我注意到的另一件事是,它没有尽快发送所有的数据,它所做的只是等到浏览器说它想要更多的数据(而不是立即发送)。
当其他设备试图同时观看视频(或从多个选项卡观看)时,我没有注意到出现了这个错误,所以我想知道是否导致了这个错误,因为它不能创建到同一个选项卡/设备/源的两个管道。
以下是我的服务器代码:
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。
发布于 2022-09-09 21:28:59
现在,您可能已经发现了这个问题,但对于未来的googlers人员来说,我遇到了这个问题,结果发现错误的内容被写入响应(开始/结束字节不存在,所以整个文件都在编写中)。
另外,https://github.com/vercel/serve-handler/blob/master/src/index.js对于如何正确返回206/416响应也有很好的参考价值。
https://stackoverflow.com/questions/70909887
复制相似问题