今天同事在使用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             }

嘿嘿,已经完美解决该问题啦!

 

posted on 2013-01-29 10:41  FengLin  阅读(8896)  评论(10编辑  收藏  举报