在 ASP.NET 中流式传输文件的最佳方式

Posted

技术标签:

【中文标题】在 ASP.NET 中流式传输文件的最佳方式【英文标题】:Best way to stream files in ASP.NET 【发布时间】:2010-10-11 03:27:36 【问题描述】:

使用 ASP.NET 流式传输文件的最佳方式是什么?

似乎有多种方法可以解决此问题,我目前在 http 处理程序中使用 Response.TransmitFile() 方法,该方法将文件直接发送到浏览器。这用于各种事情,包括将 FLV 从 webroot 外部发送到嵌入式 Flash 视频播放器。

但是,这似乎不是一种可靠的方法。特别是 Internet Explorer (7) 存在一个奇怪的问题,浏览器在观看一两个视频后就挂起。点击任何链接等都没有任何效果,让网站再次正常运行的唯一方法是关闭浏览器并重新打开它。

这也发生在其他浏览器中,但频率要低得多。根据一些基本测试,我怀疑这与文件的流式传输方式有关......也许连接没有正确关闭,或者类似的东西。

在尝试了一些不同的事情后,我发现以下方法对我有用:

Response.WriteFile(path);
Response.Flush();
Response.Close();
Response.End();

这解决了上面提到的问题,并且观看视频不再导致 Internet Explorer 挂起。

但是,我的理解是 Response.WriteFile() 首先将文件加载到内存中,并且考虑到某些正在流式传输的文件可能非常大,这似乎不是一个理想的解决方案。

我很想听听其他开发人员如何在 ASP.NET 中流式传输大文件,尤其是流式传输 FLV 视频文件。

【问题讨论】:

这是我使用的一种方法,它添加了可恢复的下载功能,如果流式传输视频将非常有用:***.com/a/6475414/222748 【参考方案1】:

尝试将文件作为流打开,然后使用 Response.OutputStream.Write()。例如:

编辑:糟糕,我忘记了 Write 需要一个字节缓冲区。固定

byte [] buffer = new byte[1<<16] // 64kb
int bytesRead = 0;
using(var file = File.OpenRead(path))

   while((bytesRead = file.Read(buffer, 0, buffer.Length)) != 0)
   
        Response.OutputStream.Write(buffer, 0, bytesRead);
   

Response.Flush();
Response.Close();
Response.End();

编辑 2:你试过了吗?它应该工作。

【讨论】:

谢谢,但我认为这不起作用... Response.OutputStream.Write 不采用 Stream 参数(无论如何在 ASP.NET 2.0 中都没有),只有一个字节数组,这将需要先将文件加载到内存中,因此并不比 WriteFile 解决方案更好。 关于字节数组——您不必将整个流加载到内存中。我的代码在流式传输期间最多使用 64kb 的内存(加上少量开销)。 如何将流响应分配给 html Video 控件?【参考方案2】:

我会把东西放在“aspx”管道之外。特别是,我会编写一个运行的处理程序(ashx,或通过配置映射),它可以完成 minimum 的工作,并简单地以块的形式写入响应。处理程序将正常接受来自查询字符串/表单的输入,定位要流式传输的对象,并流式传输数据(在循环中使用中等大小的本地缓冲区)。一个简单(不完整)的例子如下所示:

public void ProcessRequest(HttpContext context) 
    // read input etx
    context.Response.Buffer = false;
    context.Response.ContentType = "text/plain";
    string path = @"c:\somefile.txt";
    FileInfo file = new FileInfo(path);
    int len = (int)file.Length, bytes;
    context.Response.AppendHeader("content-length", len.ToString());
    byte[] buffer = new byte[1024];
    Stream outStream = context.Response.OutputStream;
    using(Stream stream = File.OpenRead(path)) 
        while (len > 0 && (bytes =
            stream.Read(buffer, 0, buffer.Length)) > 0)
        
            outStream.Write(buffer, 0, bytes);
            len -= bytes;
        
    

【讨论】:

无缘无故投反对票对任何人都没有帮助。我觉得你的回答很好。 +1 让您恢复平价。 我不是投反对票的人,但我想我会指出最初的问题确实表明代码正在http处理程序中使用......不过还是感谢您的建议。 更改文件名,在行后:context.Response ... 添加此行:context.Response.AppendHeader("content-Disposition", "attachment;filename="+filename); @MarcGravell 应该已经解释了为什么 Response.Buffer 是您的 Response.Flush 在他的回复中的论文的关键。 视频 FLV 文件的“内容类型”是什么?【参考方案3】:

在尝试了许多不同的组合(包括各种答案中发布的代码)之后,似乎在调用 TransmitFile 之前设置 Response.Buffer = true 可以解决问题,并且 Web 应用程序现在在 Internet Explorer 中的响应速度更快。

在这种特殊情况下,SWF 扩展也映射到 ASP.NET,我们在 Web 应用程序中使用自定义处理程序从磁盘读取文件,然后使用 Response.TransmitFile() 将它们发送到浏览器.我们有一个基于 Flash 的视频播放器来播放也是 SWF 的视频文件,我认为让所有这些活动通过处理程序而不进行缓冲可能会导致 IE 中发生奇怪的事情。

【讨论】:

【参考方案4】:

看看下面的文章Tracking and Resuming Large File Downloads in ASP.NET,它会给你更多的深度,而不是仅仅打开一个流并扔掉所有的位。

http 协议支持范围字节请求和可恢复下载,许多流媒体客户端(如视频播放器或 Adob​​e pdf)可以并且会尝试将它们分块,从而节省带宽并为您的用户提供更好的体验。

这不是微不足道的,但值得花时间。

【讨论】:

以上是关于在 ASP.NET 中流式传输文件的最佳方式的主要内容,如果未能解决你的问题,请参考以下文章

在 ASP.NET 中流式传输大文件上传

如何在 ASP.Net MVC 中实现视频文件流式传输?

使用 ASP.NET Core 3 流式传输视频

如何使用 ASP.NET Core 进行流式传输

csharp 在ASP.NET中流式传输和下载RDLC报告

如何从 ASP.NET MVC 控制器操作流式传输 MP3