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

20

我有一个ASP.NET MVC应用程序,其中一个页面允许用户上传文件。这些文件将有几百兆字节。

我在客户端使用FineUploader,如果浏览器支持,它将使用FileAPI/XHR,否则将回退到Iframe/form并使用enctype="multipart whatever"。

因此,在服务器端,我需要评估Request.Files.Count > 1。如果true,那么这是旧式的上传方式,我会保存文件如Request.Files[0].InputStream.CopyTo(myFileStream),否则我会执行Request.InputStreawm.CopyTo(myFileStream)

这里是一些真实代码,可以做到这些事情:https://github.com/ronnieoverby/file-uploader/blob/master/server/ASP.NET%20MVC%20C%23/FineUpload.cs

这一切都运行良好,但在我的测试中,我注意到一个问题,即无论是ASP.NET MVC控制器操作还是HttpHandler,直到整个文件上传完毕,它们才开始处理,如果文件非常大,则会占用很多Web服务器的RAM。

我找到了这篇文章:Streaming large file uploads to ASP.NET MVC,听起来很有前途,但我真的不知道代码在他的应用程序中位于哪个位置。

所以,问题是:如何在ASP.NET上传文件时将上传的文件流式传输到磁盘上?

更新

我刚看到一个关键细节,以前没有沉淀下来。来自HttpPostedFile文档:

默认情况下,所有请求(包括表单字段和上传的文件)大于256 KB都会缓冲到磁盘上,而不是保存在服务器内存中。

好的,这解决了担心在大型上传期间Web服务器的RAM利用率可能会飙升的问题。但是,仍然存在一个问题:在文件完全传输到Web服务器之后,服务器必须花费时间将其移动到最终目的地。如果文件系统操作是复制(如果目标位于另一物理磁盘上,则保证),则响应会不必要地延迟。

老实说,我可以通过增加上传处理程序/操作的响应超时来应对这个问题。但是,直接将字节流传输到它们的目标位置会更好。


我认为与 Web 服务器有关。在某个地方(现在找不到了),我读到有人创建了一个 HttpModule,可以将大型上传文件随着请求流式传输时保存到磁盘上,然后将控制权传递给 MVC 操作方法。如果我能实现这一点,那将是终极目标。 - Ronnie Overby
问题将大文件上传流式传输到ASP.NET MVC中@RedFilter的第二个答案也看起来很有前途。如果你错过了它,我也会去看看。 - CokoBWare
1
你可以在这里找到一个非常好的HttpModule示例,用于大文件上传 https://github.com/maxpavlov/jQuery-File-Upload,它是为bluimp jQuery文件上传器开发的,但很容易适应于您的文件上传工具。 - Luke Baughan
@bUKaneer 我想你指的是这个,它是一个处理程序而不是模块:https://github.com/maxpavlov/jQuery-File-Upload.MVC3/blob/master/jQuery-File-Upload.MVC3/Upload/UploadHandler.ashx.cs - Ronnie Overby
还值得一看的是这篇文章(实际上是关于Web API的,但许多观点 - 如果不是全部都是有效和可转移的!)http://www.strathweb.com/2012/09/dealing-with-large-files-in-asp-net-web-api/ - Luke Baughan
显示剩余4条评论
2个回答

6
你可以使用 HttpRequest.GetBufferlessInputStream 方法以完全自定义的方式处理上传,而无需缓冲。基本上,你可以获得对原始传入数据的访问权,并且可以自由地对其进行任何操作。
我刚刚创建了一个小样例,将原始请求内容保存到文件中:
  1. Create handler:

    public class UploadHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            using (var stream = context.Request.GetBufferlessInputStream())
            using (var fileStream = File.Create("c:\\tempfile.txt"))
            {
                stream.CopyTo(fileStream);
            }
        }
        public bool IsReusable { get {  return true; } }
    }
    
  2. Register in Web.config:

    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
        <handlers>
            <add name="UploadHandler" verb="POST"
                path="/upload"
                type="UploadHandler"
            resourceType="Unspecified"/>
        </handlers>
    </system.webServer>
    
  3. Create a page with a form:

<form action="/upload" method="post" enctype='multipart/form-data'>
     <input type="file" name="aa" id="aa"/>
     <input type="submit"/>
</form>

1
@Sergey Zyuzin。感谢分享代码。但这也会受到IIS 2GB限制的限制,对吧? - smedasn

1
如果上传和流媒体占用了宝贵的服务器资源,那么您可能希望考虑在某种类型的云上托管媒体文件。在ASP.NET中使用Rackspace、Amazon Cloud API,让用户直接将文件上传到CDN网络,然后以这种方式提供内容是可能的。我知道这不是回答你的问题,但很多人已经或者将会回答,我想说我的看法。令我惊讶的是,很多人仍然选择不使用云!一旦您使用CDN,您就永远不会回头了。此外,大多数CDN还将为您的上传容器提供流媒体URL,支持许多不同的电影类型,速度非常快,不仅对于用户上传而且对于您的网站速度也没有影响。

我的情况下文件非常敏感,我绝对不能将它们放在CDN上。 - Ronnie Overby

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接