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

问题描述:

我有一个ASP.NET MVC应用程序,该应用程序的页面允许用户上传文件.这些文件将是几百兆.

I have an ASP.NET MVC application with a page that allows users to upload files. The files will be several hundred megabytes.

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

I am using FineUploader on the client side, which will use FileAPI/XHR if the browser supports it, otherwise will fallback to Iframe/form with enctype="multipart whatever".

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

So on the server side I need to evaluate Request.Files.Count > 1. If true, this is an old school upload and I save the file like Request.Files[0].InputStream.CopyTo(myFileStream) otherwise I do Request.InputStreawm.CopyTo(myFileStream).

以下是我编写的一些实现此目的的实际代码:

Here's some of the actual code I've written that does this stuff: https://github.com/ronnieoverby/file-uploader/blob/master/server/ASP.NET%20MVC%20C%23/FineUpload.cs

这一切都很好,但是在我的测试中,我注意到ASP.NET MVC控制器动作和HttpHandler都不会开始处理,直到整个文件上传完毕,如果文件很大,这很糟糕,因为这意味着占用了大量Web服务器的RAM.

This all works fine, but in my testing I've noticed that neither an ASP.NET MVC controller action nor an HttpHandler will begin processing until the entire file is uploaded, which is bad if the file very large because that means it's occupying a lot of the web server's RAM.

我发现了这一点:将大文件上传流式传输到ASP.NET MVC 听起来很有希望,但是我真的不知道代码在他的应用程序中的位置.

I found this: Streaming large file uploads to ASP.NET MVC which sounds promising, but I really don't have an idea of where the code resides in his application.

因此,问题是:如何在ASP.NET中仍进行上传的同时将上传的文件流式传输到磁盘?

So, the question is: how to stream uploaded files to disk while the upload is still taking place in ASP.NET?

我刚刚看到一个关键细节,以前没有发现过.从 HttpPostedFile 文档:

I just saw a key detail that didn't sink in before. From the HttpPostedFile documentation:

默认情况下,所有请求,包括表单字段和上传的文件, 大于256 KB的数据将缓冲到磁盘,而不是保存在服务器中 记忆.

By default, all requests, including form fields and uploaded files, larger than 256 KB are buffered to disk, rather than held in server memory.

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

Ok, that addresses the concern that the web server's RAM utilization could spike during a large upload. But, there's still a problem: After the file is completely transferred to the web server, the server has to spend time moving it to it's final destination. If the file system operation is a copy (guaranteed if the destination is on another physical disk), then the response is delayed unnecessarily.

老实说,我可以通过增加上载处理程序/操作的响应超时来忍受这一点.但是,最好将字节直接流式传输到目的地.

Honestly, I could probably live with this by increasing response timeout for the upload handler/action. But, it would be nice to stream the bytes directly to their destination.

您可以使用完全自定义的方式处理上传,而无需使用 HttpRequest.GetBufferlessInputStream 方法.基本上,您可以访问原始的传入数据,并可以随意使用它进行任何操作.

You can handle uploads in a completely customized way without buffering using HttpRequest.GetBufferlessInputStream method. Basically you are getting access to the raw incoming data and free to do whatever you want with it.

我刚刚创建了一个小样本,将原始请求内容保存到文件中

I've just created small sample which saves raw request content to a file:

  1. 创建处理程序:

  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; } }
}

  • 在Web.config中注册:

  • Register in Web.config:

    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true"/>
        <handlers>
            <add name="UploadHandler" verb="POST"
                path="/upload"
                type="UploadHandler"
            resourceType="Unspecified"/>
        </handlers>
    </system.webServer>
    

  • 使用以下格式创建页面:

  • 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>