14

我有一个应用程序,旨在从我们的本地数据库流式传输视频。昨天我花了很多时间试图返回数据 aRangeFileContentResultRangeFileStreamResult没有成功。

简而言之,当我将文件作为这两个结果中的任何一个返回时,我似乎无法正确播放视频(或根本无法播放)。

来自浏览器的请求使用以下标头发送:

Range: bytes=0-

提供的响应以这些标头为例:

Accept-Ranges: bytes
Content-Range: bytes 0-5103295/5103296

在网络流量方面,我得到了一系列 206 的部分结果,最后是 200(根据提琴手),这似乎是正确的。Chrome 的网络选项卡不同意这一点,并看到一个初始请求(我认为是握手总是 13 个字节),然后是几个状态为已取消或待处理的请求。据我了解,这或多或少是正确的,206 - 取消,206 - 取消等。但视频永远不会播放。

如果我将结果从我的控制器切换到 FileResult,视频播放和 Chrome、IE10 和 Firefox,并且似乎在下载结束之前开始播放(感觉有点像流媒体!虽然我怀疑不是)

但是对于范围结果,我在 chrome 或 IE 中一无所获,并且在 Firefox 中一滴就能下载整个视频。

据我了解,RangeFileContentResult应该处理响应客户端并下载一系列字节(我的似乎没有这样做,它只是告诉它获取整个文件(如上面的响应所示))。客户应该对此做出回应,但它似乎没有这样做。

有人对这方面有任何想法吗?具体来说:

a) 应该RangeFileContentResult将一系列字节发送回客户端?b)有什么方法可以显式控制从客户端请求的字节范围?c)在请求时,是否有任何原因或我做错了什么会导致浏览器根本不加载视频RangeFileContentResult

编辑:添加了一个图表来帮助描述我所看到的:

范围请求图像

EDIT2:好的,所以情节变厚了。在使用 RangedFile gubbins 时,我们需要推出另一个系统测试版本,我在控制器操作中留下了“RangeFileContentResult”,如下所示:

private ActionResult RetrieveVideo(MediaItem media)
{
    return new RangeFileContentResult(
        media.Content, 
        media.MimeType, 
        media.Id.ToString(), 
        DateTime.Now);            
}

相当奇怪的是,这现在似乎在我们的 Azure 系统测试环境上按预期工作,但在我的本地计算机上仍然没有。我想知道是否有一些基于 IIS 的东西在 Azures IIS8 上运行良好,但在我的本地 7.5 实例上却没有?

4

3 回答 3

6

这里描述的问题的原因是传递给构造函数modificationDate参数的值RangeFileContentResult

return new RangeFileContentResult(media.Content, media.MimeType, media.Id.ToString(), DateTime.Now); 

该日期被用于RangeFileResult创建两个标题:

  • ETag- 此标头是浏览器和服务器使用的标识符,以确保它们谈论的是同一实体。
  • Last-Modified- 此标头通知浏览器有关实体的最后修改日期。

DateTime.Now每次浏览器发出部分请求时都会传递a 的事实可能是在客户端获取整个实体之前更改标头值ETag的原因Last-Modified(通常如果整个过程花费的时间超过一秒)。

在上述情况下,浏览器正在发送If-Range请求的标头。If-Range此标头告诉服务器,如果实体标签(或修改日期,因为可以携带这两个值之一)不多,则应该重新发送整个实体。这就是在这种情况下发生的事情。

如果客户端决定使用以下标头之一进行验证,则修改日期是“动态的”这一事实也可能导致进一步的问题:If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match

这种情况下的解决方案是在数据库中与文件保持一个修改日期,以确保它是一致的。

这里也有优化的地方。不是每次发出部分请求时都从数据库中抓取整个视频,而是可以缓存它或只抓取相关部分(如果应用程序正在使用的数据库引擎允许这样的操作)。这种机制可以用于通过传递RangeFileResult和覆盖WriteEntireEntityWriteEntityRange方法来创建专门的动作结果。

于 2013-11-27T15:01:58.500 回答
4

好的所以我没有足够的时间详细查看 RangeFileResult,但我刚刚从 RangeFileContentResult下载了文件 (RangeFileContentResult)

并修改了我的代码,看起来像

public ActionResult Movie()
{
    byte[] file = System.IO.File.ReadAllBytes(@"C:\HOME\asp\Java\Java EE. Programming Spring 3.0\01.avi");

    return new RangeFileContentResult(file, "video/x-msvideo", "01.avi", DateTime.Now);
}

它再次起作用。但是,我注意到当我停止视频时出现异常,它发生在 RangeFileResult

if (context.HttpContext.Response.IsClientConnected)
{
    WriteEntityRange(context.HttpContext.Response, RangesStartIndexes[i], RangesEndIndexes[i]);
    if (MultipartRequest)
                context.HttpContext.Response.Write("\r\n");
    context.HttpContext.Response.Flush();
}

所以你最好修改代码来处理它。当用户已经断开连接,但你仍在尝试向他们发送响应时。

同样,从技术上讲,无论您传递 byte[] 还是 Stream 并没有太大区别,因为即使您传递 Stream 使用它的代码

using (FileStream)
{
    FileStream.Seek(rangeStartIndex, SeekOrigin.Begin);

    int bytesRemaining = Convert.ToInt32(rangeEndIndex - rangeStartIndex) + 1;
    byte[] buffer = new byte[_bufferSize];

    while (bytesRemaining > 0)
    {
        int bytesRead = FileStream.Read(buffer, 0, _bufferSize < bytesRemaining ? _bufferSize : bytesRemaining);
        response.OutputStream.Write(buffer, 0, bytesRead);
        bytesRemaining -= bytesRead;
    }
}

再次读取数据并将它们放入 byte[] 数组中!....所以这取决于你!

但是...我建议您注意您提供的内容类型!!!关键是您的浏览器必须能够处理它!因此,如果您提供一些未知的内容,您肯定会遇到问题。要查找您的内容类型字符串,请检查 mime-types-by-content-type

我再次快速浏览了一下,如果您有问题,我稍后会在回家时帮助您。

于 2013-11-05T22:41:26.233 回答
3

mofi请把这两个文件复制到你的mvc项目
RangeFileResult
RangeFileStreamResult

public ActionResult Movie()
{
    var path = new FileStream(@"C:\temp\01.avi", FileMode.Open);
    return new RangeFileStreamResult(path, "video/x-msvideo", "01.avi", DateTime.Now);
}

现在运行您的项目并在 chrome 中打开(例如:http: //youraddress.com :45454/Main/Movie ),您应该会看到您的文件正在使用标准的 chrome 视频播放器播放。它正在流式传输,如果您在

return new RangeFileStreamResult(path, "video/x-msvideo", "01.avi", DateTime.Now);

同样,源很容易修改以更改用于流式传输的缓冲区大小!

于 2013-11-05T12:32:52.670 回答