今天同事在使用ASP.NET WebApi開發一個文件上傳功能時發現WebApi無法實現大文件上傳功能,於是向我求助。
當我知道這個問題的時候第一反應是WebApi不可能不支持,於是就開始查看他的代碼:
View Code
1 var config = new HttpSelfHostConfiguration("http://localhost:8080"); 2 3 config.Routes.MapHttpRoute( 4 name: "DefaultApi", 5 routeTemplate: "api/{controller}/{action}" 6 ); 7 8 HttpSelfHostServer server = new HttpSelfHostServer(config); 9 10 server.OpenAsync().Wait(); 11 12 Console.WriteLine("Server Started..");
發現如下問題:
1.MaxReceivedMessageSize沒有更改為更大的值,默認為65536,當接收消息超過這個數值時服務器將會終止接收。
2.發現並未更改傳輸模式,即TransferMode,TransferMode有兩種模式,一種為Buffer模式,Buffer模式會將文件接收到緩沖區中,直到文件完整接收完成才會交由程序處理,
當上傳的文件大小比Buffer還要大的時候,由於文件尚未接收完成緩沖區已經滿了,因此會發生異常。TransferMode的Stream模式解決了這個問題,Stream與Buffer模式不同,Stream模式能夠支持文件
一邊接收一邊保存,這樣就可以解決大文件傳輸的問題了。
但是有人會問那Buffer模式有什么用?其實Buffer模式也有它的應用場景,一般用於可靠傳輸,即要不全部接收,要不都不接收。猜測WS里的可靠傳輸就是這么實現的。
修改后代碼:
View Code
1 var config = new HttpSelfHostConfiguration("http://localhost:8080"); 2 3 //設置最大接收消息大小 4 config.MaxReceivedMessageSize = int.MaxValue; 5 //將默認緩沖形式的數據傳輸模式改為流模式, 緩沖模式需要將所有數據接收完成后才會寫入, 流模式可以一邊接收一邊寫入一般用於大數據量文件傳輸 6 config.TransferMode = TransferMode.Buffered; 7 8 config.Routes.MapHttpRoute( 9 name: "DefaultApi", 10 routeTemplate: "api/{controller}/{action}" 11 ); 12 13 HttpSelfHostServer server = new HttpSelfHostServer(config); 14 15 server.OpenAsync().Wait(); 16 17 Console.WriteLine("Server Started..");
代碼修改后大文件接收成功,但是發現接收后的文件無法打開或者格式錯誤等問題,查看同事開發的客戶端,是使用WebClient.UploadFile方法上傳文件的,Upload方法使用Post方式上傳,因此使用Fiddler模擬文件上傳請求,發現使用Post方法上傳文件時Http Request Body中存在一些分隔符類的字符
Content-Type: multipart/form-data; boundary=-------------------------acebdf13572468
User-Agent: Fiddler
---------------------------acebdf13572468
Content-Disposition: form-data; name="fieldNameHere"; filename="IMG_2116.JPG"
Content-Type: image/jpeg
<@INCLUDE *C:\Users\kuangfenlin\Desktop\IMG_2116.JPG*@>
---------------------------acebdf13572468--
通過Google發現這其實是一個同時上傳多個文件的標准,多個文件或內容通過 boundary=-------------------------acebdf13572468 進行分割。
這樣就解釋了為什么文件接收后無法打開的問題,查看WebApi源代碼發現已經存在 MultipartFormDataStreamProvider 類型專門用於處理這種情況,修改Action代碼:
View Code
1 if (!Request.Content.IsMimeMultipartContent()) 2 { 3 throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); 4 } 5 6 string root = @"C:/"; 7 var provider = new MultipartFormDataStreamProvider(root); 8 9 try 10 { 11 StringBuilder sb = new StringBuilder(); 12 13 var task = Request.Content.ReadAsMultipartAsync(provider); 14 15 task.Wait(); 16 17 foreach (var file in provider.FileData) 18 { 19 FileInfo fileInfo = new FileInfo(file.LocalFileName); 20 sb.Append(string.Format("Uploaded file: {0} ({1} bytes)\n", fileInfo.Name, fileInfo.Length)); 21 } 22 return new HttpResponseMessage() 23 { 24 Content = new StringContent(sb.ToString()) 25 }; 26 } 27 catch (System.Exception e) 28 { 29 return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e); 30 }
嘿嘿,已經完美解決該問題啦!
