【问题标题】:Serving large files with C# HttpListener使用 C# HttpListener 提供大文件
【发布时间】:2012-11-03 08:25:36
【问题描述】:

我正在尝试使用 HttpListener 来提供静态文件,这适用于小文件。当文件变大时(使用 350 和 600MB 文件进行测试),服务器因以下异常之一而阻塞:

HttpListenerExceptionI/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,但如果可能的话,更可能远离)。

【问题讨论】:

    标签: c# httplistener


    【解决方案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 有什么好处吗?
    • 与问题相比,这里的最大区别是什么?能否总结一下问题和解决方法?
    猜你喜欢
    • 2014-04-10
    • 2020-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-27
    相关资源
    最近更新 更多