使用 C# HttpListener 提供大文件
Posted
技术标签:
【中文标题】使用 C# HttpListener 提供大文件【英文标题】:Serving large files with C# HttpListener 【发布时间】:2012-11-03 08:25:36 【问题描述】:我正在尝试使用 HttpListener 来提供静态文件,这适用于小文件。当文件变大时(使用 350 和 600MB 文件进行测试),服务器因以下异常之一而阻塞:HttpListenerException:I/O 操作已中止由于线程退出或应用程序请求,或者:HttpListenerException:信号量超时期限已过期。
需要改变什么来摆脱异常,让它运行稳定/可靠(和快速)?
这里有一些进一步的阐述:这基本上是this earlier question 的后续问题。代码略微扩展以显示效果。内容写入循环使用(希望是合理的)块大小,在我的情况下为 64kB,但更改值除了速度之外没有任何区别(请参阅前面提到的旧问题)。
using( FileStream fs = File.OpenRead( @"C:\test\largefile.exe" ) )
//response is HttpListenerContext.Response...
response.ContentLength64 = fs.Length;
response.SendChunked = false;
response.ContentType = System.Net.Mime.MediaTypeNames.Application.Octet;
response.AddHeader( "Content-disposition", "attachment; filename=largefile.EXE" );
byte[] buffer = new byte[ 64 * 1024 ];
int read;
using( BinaryWriter bw = new BinaryWriter( response.OutputStream ) )
while( ( read = fs.Read( buffer, 0, buffer.Length ) ) > 0 )
Thread.Sleep( 200 ); //take this out and it will not run
bw.Write( buffer, 0, read );
bw.Flush(); //seems to have no effect
bw.Close();
response.StatusCode = ( int )HttpStatusCode.OK;
response.StatusDescription = "OK";
response.OutputStream.Close();
我正在浏览器中尝试下载,也尝试在使用 HttpWebRequest 的 C# 程序中下载,这没有区别。
根据我的研究,我认为 HttpListener 并不能真正将内容刷新到客户端,或者至少以它自己的速度这样做。我也省略了 BinaryWriter 并直接写入流 - 没有区别。在基本流周围引入了 BufferedStream - 没有区别。有趣的是,如果在循环中引入了 Thread.Sleep(200) 或稍大一些,它可以在我的盒子上运行。但是我怀疑它对于真正的解决方案是否足够稳定。 This question 给人的印象是根本没有机会让它正常运行(除了转移到我会求助的 IIS/ASP.NET,但如果可能的话,更可能远离)。
【问题讨论】:
【参考方案1】:您没有向我们展示如何初始化 HttpListener 的另一个关键部分。因此,我使用下面的代码尝试了您的代码,并且成功了
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://*:8080/");
listener.Start();
Task.Factory.StartNew(() =>
while (true)
HttpListenerContext context = listener.GetContext();
Task.Factory.StartNew((ctx) =>
WriteFile((HttpListenerContext)ctx, @"C:\LargeFile.zip");
, context,TaskCreationOptions.LongRunning);
,TaskCreationOptions.LongRunning);
WriteFile
是您删除 Thread.Sleep( 200 );
的代码。
如果你想看它的完整代码。
void WriteFile(HttpListenerContext ctx, string path)
var response = ctx.Response;
using (FileStream fs = File.OpenRead(path))
string filename = Path.GetFileName(path);
//response is HttpListenerContext.Response...
response.ContentLength64 = fs.Length;
response.SendChunked = false;
response.ContentType = System.Net.Mime.MediaTypeNames.Application.Octet;
response.AddHeader("Content-disposition", "attachment; filename=" + filename);
byte[] buffer = new byte[64 * 1024];
int read;
using (BinaryWriter bw = new BinaryWriter(response.OutputStream))
while ((read = fs.Read(buffer, 0, buffer.Length)) > 0)
bw.Write(buffer, 0, read);
bw.Flush(); //seems to have no effect
bw.Close();
response.StatusCode = (int)HttpStatusCode.OK;
response.StatusDescription = "OK";
response.OutputStream.Close();
【讨论】:
噢!我不得不承认我已经陷入了一种痴迷,即 HttpListener 可能存在一些问题,而不是只是想清楚。所以@L.B 谢谢你让我回到正确的轨道上。基本上我正在以相同的方式实例化 HttpListener ,并且我确实使用您的代码得到了错误。当然,我已经在我们环境中的不同 PC 上进行了尝试,并且看到了相同的效果,但是在干净的 VM 机器上,您的代码和我的原始实现都可以完美运行。我还没有找出原因是什么,我怀疑杀毒软件......所以再次感谢您的帮助! @L.B.:你能说明使用额外的 BinaryWriter 有什么好处吗? 与问题相比,这里的最大区别是什么?能否总结一下问题和解决方法?以上是关于使用 C# HttpListener 提供大文件的主要内容,如果未能解决你的问题,请参考以下文章
如何在 C# 中的随机端口上创建 HttpListener 类?
C# 中 HttpListener 和 Griffin.WebServer 的替代方案