繁体   English   中英

尝试使用管道从 Node.JS 服务器获取 stream 视频时出现错误 ERR_STREAM_PREMATURE_CLOSE

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM