WebAPI上傳大文件


今天在研究WebAPI的上傳與下載,作為Rest的框架,更多是面向資源,就其本身來說,是不會涉及也不應該涉及到大文件的處理,具體多大呢,也就是ASP.NET的限值2G。

ASP.NET的pipeline對於上傳文件一般的處理流程是接收到文件,放到內存中,我們也一般只處理后續的流程,例如存入硬盤等等。

目前想象的一個場景是上傳一個大文件,后續處理可能有多種。如果並發數過多,肯定會造成內存溢出,所以參考資料與琢磨,搞定了這個問題。

 

但是場景本身是有一定問題的,這純屬個人愛好在玩,真實場景來說文件不宜過大,不是專門處理大文件的服務器與協議,無非就是用流的方式,但是大文件傳輸使用流采用可靠協議是最好的,可靠不是指TCP的可靠,而是指在如果斷開后,后續的處理,例如斷點。

如果是HTTP的斷點,那就只有自己寫了。

 

一、配置文件修改

不多說,這個必須的。

1   <system.web>
2     <compilation debug="true" targetFramework="4.5" />
3     <httpRuntime targetFramework="4.5" maxRequestLength="2147483647"/>
4   </system.web>
View Code
    <security>
      <requestFiltering >
        <requestLimits maxAllowedContentLength="2147483647" ></requestLimits>
      </requestFiltering>
    </security>
View Code

 

二、擴展主機緩存Policy

這是微軟預留的接口,作為擴展,讓用戶自己關閉主機是否緩沖,也就是正常流程中將上傳文件的內容存入內存的動作

public class NoBufferPolicySelector : WebHostBufferPolicySelector
{
   public override bool UseBufferedInputStream(object hostContext)
   {
      var context = hostContext as HttpContextBase;
 
      if (context != null)
      {
          if (context.Request.HttpMethod == HttpMethod.Post.ToString() && context.Request.ContentLength >200000)
              return false;
      }
 
      return true;
   }
View Code

 

 

還有個動作就是要去注冊它

 1         public static void Register(HttpConfiguration config)
 2         {
 3             // Web API 配置和服務
 4 
 5             // Web API 路由
 6             config.MapHttpAttributeRoutes();
 7 
 8             config.Routes.MapHttpRoute(
 9                 name: "DefaultApi",
10                 routeTemplate: "api/{controller}/{id}",
11                 defaults: new { id = RouteParameter.Optional }
12             );
13 
14 
15             GlobalConfiguration.Configuration.Services.Replace(typeof(IHostBufferPolicySelector), new NoBufferPolicySelector());
16 
17 
18         }
View Code

 

三、想怎么玩兒怎么玩兒

上傳的代碼,大家都懂的

 [RoutePrefix("test")]
    public class UploadController : ApiController
    {
        [Route("upload")]
        [HttpPost]
        public Task<IEnumerable<FileDesc>> Post()
        {
            var folderName = "uploads";
            var PATH = HttpContext.Current.Server.MapPath("~/" + folderName);
            var rootUrl = Request.RequestUri.AbsoluteUri.Replace(Request.RequestUri.AbsolutePath, String.Empty);
            if (Request.Content.IsMimeMultipartContent())
            {             
                var streamProvider = new CustomMultipartFormDataStreamProvider(PATH);
                var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith<IEnumerable<FileDesc>>(t =>
                {

                    if (t.IsFaulted || t.IsCanceled)
                    {
                        throw new HttpResponseException(HttpStatusCode.InternalServerError);
                    }

                    var fileInfo = streamProvider.FileData.Select(i =>
                    {
                        var info = new FileInfo(i.LocalFileName);
                        return new FileDesc(info.Name, rootUrl + "/" + folderName + "/" + info.Name, info.Length / 1024);
                    });
                    return fileInfo;
                });

                return task;
            }
            else
            {
                throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "This request is not properly formatted"));
            }
        }
    }

    public class CustomMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
    {
        public CustomMultipartFormDataStreamProvider(string path)
            : base(path)
        { }

        public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
        {
            var name = !string.IsNullOrWhiteSpace(headers.ContentDisposition.FileName) ? headers.ContentDisposition.FileName : "NoName";
            return name.Replace("\"", string.Empty); 
        }
    }
View Code

MultipartFormDataStreamProvider

還有各種Provider,這樣我們就可以有很多種處理方式,去掉WebAPI自帶的Buffer,我們自己來處理。

MultipartFormDataRemoteStreamProvider 例如這個Provider,是抽象類,需要自己繼承,設想如果我們要把這個上傳的文件傳到另外的地方去,例如Azure上,我們是不是可以直接架設管道,用流的方式。

下次介紹Provider。

 

 

最后還是上圖吧,without picture I say ge hair?

 

准備工作,要上傳的文件,才裝了系統,就用這個吧

 

700多MB夠了吧,還要怎樣?

 

准備客戶端,用網頁比較方便,直接看進度

<h2>API Upload</h2>
<form name="apiForm" method="post" enctype="multipart/form-data" action="http://localhost:15068/test/upload">
<div>
<label for="apifiles">Select a File</label>
<input name="apifiles" type="file" />
</div>
<div>
<input type="submit" value="Upload" />
</div>
</form>

 

那就開始吧

 

 

 

 

 

至於內存的占用,既然是流,就不用多說了。不會有很大起伏的,就不貼圖了,已經占了很大版面了。

 

 

 

 

PS:在學習的過程中,還遇到另外一種解決方案,是否還記得ASP.NET中接收上傳文件的SaveAS方法,是的,用這個方法的主人也是可以接收文件的,它會干一個有趣的事情,不用內存作為Buffer,而是用硬盤,並且IIS在關閉或者隔一段時間后會干掉這個硬盤Buffer,后續的流程還是一樣。對了,這是在IIS上測試的,本地宿主沒試過。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM