【问题标题】:Why won't <audio> elements loop for files hosted on a node.js server?为什么 <audio> 元素不会为 node.js 服务器上托管的文件循环?
【发布时间】:2013-06-02 22:55:36
【问题描述】:

我正在使用托管在 node.js 服务器上的以下 HTML 进行测试:

<!doctype html>
<html>
<head>
    <meta charset='utf-8'>
    <title>Audio Testing</title>
</head>
<body>
    <audio src='/public/tests/audioTest.mp3' controls autoplay loop></audio>
</body>
</html>

问题是页面加载时音频只会播放一次(仅限 Chrome,因为它是 mp3)。

我怀疑这是一个 node.js 问题,因为当我通过在没有任何网络服务器的情况下仅加载 HTML 文件进行本地测试时,音频循环正常。它也可以在我测试过的 Apache 服务器上按预期工作。此外,如果我链接到托管在 Apache 服务器上的 mp3 文件,它仍然可以与节点服务器提供的 HTML 一样正常工作。

编码为base64 时,音频也可以正常循环播放。

我使用的是 node.js 的 0.8.14 版本。托管在节点上的代码的现场演示可以访问here

编辑 服务器有一个名为“public”的目录,如果有请求,服务器将始终从该目录中提供文件。音频文件和html页面都在“public”目录下。

node.js服务端代码如下:

var http = require('http');
var fs = require('fs');
var mime = require('mime');//Third party library for looking up mime types
var handleRequest = function(req, res) {
  if (req.url === '/') {
    fs.createReadStream(__dirname+'/public/homePage.html').pipe(res);
    return;
  }
  else if (req.url === '/favicon.ico') {
    fs.createReadStream(__dirname+'/public/favicon.ico').pipe(res);
    return;
  }
  else if (req.url.substr(0, 7) === '/public') {
    var mim = mime.lookup(req.url);
    var ext = mime.extension(mim);
    console.log('mime '+mim+' ext '+ext);
    if (ext !== 'bin') {
        fs.exists(__dirname+'/'+req.url, function(exists) {
            if (exists) {
                    if (req.headers.range) {
        var filename = __dirname+req.url;
        fs.readFile(filename, 'binary', function(err, file) {
          var header = {};
          var range = req.headers.range; 
          var parts = range.replace(/bytes=/, "").split("-"); 
          var partialstart = parts[0]; 
          var partialend = parts[1]; 

          var total = file.length; 

          var start = parseInt(partialstart, 10); 
          var end = partialend ? parseInt(partialend, 10) : total-1;

          header["Content-Range"] = "bytes " + start + "-" + end + "/" + (total);
          header["Accept-Ranges"] = "bytes";
          header["Content-Length"]= (end-start)+1;
          //header['Transfer-Encoding'] = 'chunked';
          header["Connection"] = "close";

          res.writeHead(206, header); 
          res.write(file.slice(start, end)+'0', "binary");
          res.end();
          return;
        });
      }
      else {
        res.writeHead(200,{'Content-Type':mim});
        fs.createReadStream(__dirname+'/'+req.url).pipe(res);
        return;
      }
            else {res.end();}
        });
    }
    else {res.end();}
  }
  else {res.end();}
};
var server = http.createServer(handleRequest);
server.listen(8888);//Listening on port 8888

【问题讨论】:

  • 当你说你在“Node.js 服务器”上托管一个文件时,这并不意味着什么,因为你可以用 Node.js 编写任何你想要的东西。如果您使用的是内置 HTTP 类,请确保您具体提及您正在使用的内置 HTTP 类,并提供您在 Node.js 中使用的实际代码,因为这就是您认为问题所在。
  • 看起来可以通过经典的../../../../../etc/passwd 或其他方式从磁盘中检索任何文件。
  • @Brad 我不希望我的文件那么容易检索!我知道这是一个单独的问题,但我能做些什么来阻止这种简单的检索方法?
  • 对于您正在做的事情,我实际上建议采用 Express。 expressjs.com/guide.html 它非常适合您正在做的事情,如果您使用它的 static 模块,这些保护措施已经到位。它甚至可以处理etag 的缓存内容,并且可能会执行范围请求,但我不确定。如果您不想使用 Express,只需添加一个检查以确保您从磁盘加载的任何文件实际上都位于设置为文档根目录的目录下。
  • @Brad 非常感谢!

标签: node.js audio


【解决方案1】:

我怀疑这是因为您的 Node.js 服务器没有响应范围请求。

Chrome 正在为文件中的特定字节集发送范围请求。我不知道为什么如果没有在响应中获取这些字节就无法循环,但这应该是唯一的区别。

【讨论】:

  • 音频文件实际上是在发送范围标头。处理此问题的适当方法是什么:req.headers.range 在请求音频文件时返回“bytes=0-”。
  • @MichaelHarbach,您应该使用请求的字节提供206 响应代码。 Bytes 0- 表示文件开头的所有字节。这实际上是对服务器是否能够支持范围请求的测试。然后,Chrome 通常会立即触发具有不同范围的第二个请求。在我对您的链接的测试中,我看到来自 Chrome 的对 Range: bytes=0-29130 的请求,而您的服务器仅以 200 响应状态代码,就好像它是一个正常的 HTTP 请求一样。
  • 当我发送另一个请求时它返回:bytes=0-29130
  • 好的,让我试着解决它。我将为 Content-Range 设置一个额外的标题。
  • 您能否给我一个代码 sn-p,说明我的服务器应该如何格式化响应?我正在尝试: res.writeHead(206, {'Content-Range':res.headers.range}) 但这不起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-28
  • 2011-08-16
  • 1970-01-01
  • 2011-02-15
  • 2016-12-29
  • 1970-01-01
相关资源
最近更新 更多