前段时间我在一些流媒体视频教程中发现了这个功能,因此不需要将整个文件加载到RAM中以供服务的文件(因此您可以提供大型视频文件而不会因为崩溃而崩溃超过Node.js的内存上限 - 使用电影长度的视频文件并不难超过,并且增加内存分配只是一个创可贴解决方案。)
var fs = require("fs"),
http = require("http"),
url = require("url"),
path = require("path");
var dirPath = process.cwd();
var videoReqHandler = function(req, res, pathname) {
var file = dirPath + "/client" + pathname;
var range = req.headers.range;
var positions = range.replace(/bytes=/, "").split("-");
var start = parseInt(positions[0], 10);
fs.stat(file, function(err, stats) {
if (err) {
throw err;
} else {
var total = stats.size;
var end = positions[1] ? parseInt(positions[1], 10) : total - 1;
var chunksize = (end - start) + 1;
res.writeHead(206, {
"Content-Range" : "bytes " + start + "-" + end + "/" + total,
"Accept-Ranges" : "bytes",
"Content-Length" : chunksize,
"Content-Type" : "video/mp4"
});
var stream = fs.createReadStream(file, {
start : start,
end : end
}).on("open", function() {
stream.pipe(res);
}).on("error", function(err) {
res.end(err);
});
}
});
};
module.exports.handle = videoReqHandler;
它在Chrome和FF中运行良好,但是,如果您使用Internet Explorer或Edge(新名称,相同的可怜功能支持)请求mp4,它会崩溃服务器。
var positions = range.replace(/bytes=/, "").split("-");
^
TypeError: Cannot read property 'replace' of undefined
我怀疑是因为the fact that range headers aren't mandatory, and this function requires them。
我的问题是,如何修改此功能以避免在range
标题未发送时崩溃?我对头文件或视频流不太了解,而且我对理解以块状态读取文件的方式非常不错,所以我真的可以在这个函数上使用一些帮助来解决这三个问题。
到目前为止,基于the answer @AndréDion linked:
想出来。 IE11确实支持伪流,但它想要 " Accept-Ranges:bytes"标题之前它会打扰请求a 范围,所以服务器需要响应,无论是否 它实际上是发送一个字节范围。我不得不修改我的视频流 这样做的模块。
我尝试将处理程序代码包装在:
中if (req.headers.range) {
console.log('Video request sent WITH range header!');
// ... handler code ...
} else {
console.log('Video request sent without range header!');
res.writeHead(206, {
"Accept-Ranges": "bytes"
});
res.end();
}
但似乎没有发生任何事情 - 服务器停止崩溃,并继续在其他浏览器上工作,但IE似乎没有加载视频。我期待IE等待该响应然后发送另一个请求,这次包括一个范围标题,但这似乎没有发生。
在向IE发送接受范围标头时,我可能需要发送与206不同的响应代码吗?我不知道它会是什么,但当我用accept-rages标题回复时,IE似乎重复了两次请求(但不包括范围标题,这是我需要的)。
现在看来,如果我使用上面显示的条件运行服务器,然后尝试在IE中加载一次视频,然后尝试在Chrome中加载它,我会得到这些日志:
Video request sent without range header!
Video request sent without range header!
Video request sent without range header!
Video request sent WITH range header!
IE发送三个没有范围标头的请求,Chrome当然总是按预期发送范围标头,发送一个请求并成功加载视频。我只是不确定从哪里开始。
答案 0 :(得分:2)
看起来Internet Explorer不仅仅需要Accept-Ranges标头。 This MSDN forum post表示您还需要使用返回If-Range header的ETag进行回复。像这样:
console.log('Video request sent without range header!');
res.writeHead(206, {
"Accept-Ranges": "bytes",
"If-Range": "\"<ETag#>\""
});
不幸的是,我现在无法进行任何测试,所以让我知道它是怎么回事,当我有机会时我会进一步调查并编辑我的帖子。
我接受了进一步的调查,发现If-Range不是必需的。事实上,似乎这个请求处理程序对于Internet Explorer来说不够强大。这是我的代码:
var videoReqHandler = function(req, res, pathname) {
var file = path.resolve(__dirname, pathname);
fs.stat(file, function(err, stats) {
if (err) {
if (err.code === 'ENOENT'){
//404 Error if file not found
res.writeHead(404, {
"Accept-Ranges" : "bytes",
"Content-Range" : "bytes " + start + "-" + end + "/" + total,
"Content-Length" : chunksize,
"Content-Type" : "video/mp4"
});
}
res.end(err);
}
var start;
var end;
var chunksize;
var total = stats.size;
var range = req.headers.range;
if (range) {
var positions = range.replace(/bytes=/, "").split("-");
end = positions[1] ? parseInt(positions[1], 10) : total - 1;
start = parseInt(positions[0], 10);
}else{
start = 0;
end = total - 1;
}
if (start > end || start < 0 || end > total - 1){
//error 416 is "Range Not Satisfiable"
res.writeHead(416, {
"Accept-Ranges" : "bytes",
"Content-Range" : "*/" + stats.size,
"Content-Type" : "video/mp4"
});
res.end();
return;
}
if (start == 0 && end == total -1){
res.writeHead(200, {
"Accept-Ranges" : "bytes",
"Content-Range" : "bytes " + start + "-" + end + "/" + total,
"Content-Length" : total,
"Content-Type" : "video/mp4"
});
}else{
res.writeHead(206, {
"Accept-Ranges" : "bytes",
"Content-Range" : "bytes " + start + "-" + end + "/" + total,
"Content-Length" : end - start + 1,
"Content-Type" : "video/mp4"
});
}
var stream = fs.createReadStream(file, {
start : start,
end : end
}).on("open", function() {
stream.pipe(res);
}).on("error", function(err) {
res.end(err);
});
});
};
这会添加一些缺少的错误检查,例如确保请求的范围是有效范围,如果请求没有范围标题,则返回整个文件的代码为200。还有一些事情需要做,以使其为生产服务器坚如磐石:
最后一点说明:此代码可以在IE和Edge中运行,但我确实注意到由于某种原因,他们最终请求两次FULL文件!这些请求遵循我的设置:
最后一点说明,在我添加了对完整文件进行响应的能力之前,当没有Range标题时,IE会显示200状态IE将显示带有“Decode Error”的视频的黑盒子。如果有人能告诉我原因,那就太棒了!
答案 1 :(得分:0)
据我所知,Viziionary希望坚持使用vanilla Node.js,但是对于其他任何看过这个并且不太关注Node的人来说,这里有一些不涉及手动服务的选择静态文件。
如果您想坚持使用Node,但是添加框架就可以了,the Express framework具有提供静态文件和目录的功能。使用Express一行或两行代码,您可以使用范围请求和etag支持来提供静态文件。它比编写自己的请求处理程序容易得多。
如果你在节点之外走路,你可以pair Node and Nginx together。在此配置中,Nginx是一个前端,为您的节点服务器提供静态文件和代理Web请求。事实上,Nginx was designed to work this way。请注意,您可以代理任意数量的完全不同的服务器和
你也可以在前端用Apache做这件事,我留给你查询如何。我从来没有这样做过,所以我所能做的就是指导你找一份有用的教程。
我确定还有其他配置,但这应该给你一些思考的食物。对于大多数中小型网站,您很难注意到使用一个系统或另一个系统的任何负面影响。如果您正在运行大型网站或服务,那么您最好做好功课,因为还有许多其他选项和因素需要考虑:CDN,其他脚本语言,缓存,可扩展性等等。