使用ASP.NET WEB API文档来上传异步文件

使用ASP.NET WEB API文档来上传异步文件

原文作者:Henrik F Nielsen

    HTML窗体文件上传(在RFC1867中定义)是经典的上传内容到Web服务器机制,同时我知道的所有浏览器都支持它。这个博客展示了在ASP.NET Web API文档中,如何通过使用.NET 4和增强版的.NET4.5基于任务模式,来处理窗体文件的上传。
    在使用ASP.NET Web API文档时,你可以通过托管来上传任意大小的文件。ASP.NET对于你上传的文件大小不能超过2G。
什么是HTML文件上传?
    首先提醒自己的是HTML文件上传是什么。如果你不需要修改HTML上传文件,那么你可以跳过下一节。
    您需要在一个HTML表单中启用支持HTML文件上传,这里使用的属性是enctype=”multipart/form-data”,然后有这样的“文件”类型的输入字段:

   1: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

   2: <html>

   3: <head>

   4:     <title>File Upload Sample</title>

   5: </head>

   6: <body>

   7:     <form action="http://localhost:8080/api/upload" enctype="multipart/form-data" method="POST">

   8:     What is your name?

   9:     <input name="submitter" size="40" type="text"><br>

  10:     What file are you uploading?

  11:     <input name="data" size="40" type="file">

  12:     <br>

  13:     <input type="submit">

  14:     </form>

  15: </body>

  16: </html>

    在使用MIME多部分时,这会使所有的数据进行编码,同时一个HTTP POST请求提交如下:

   1: Content-type: multipart/form-data, boundary=AaB03x

   2: 

   3: --AaB03x

   4: content-disposition: form-data; name="submitter"

   5: 

   6: Henrik Nielsen

   7: --AaB03x

   8: content-disposition: form-data ; name="data"; filename="file1.txt"

   9: Content-Type: text/plain

  10: 

  11:  ... contents of file1.txt ...

  12: --AaB03x--

    注意:如何在表单中输入字段名映射到在MIME多部分消息中的Content-Disposition头。每个表单作为自己的MIME正文部分被编码,如上面的提交者字段所示。
    在HTML5中,许多浏览器都支持通过使用多个关键字在一个表单中提交上传多个文件:

   1: <!DOCTYPE HTML>

   2: <html>

   3: <head>

   4:     <title>HTML5 Multiple File Upload Sample</title>

   5: </head>

   6: <body>

   7:     <form action="http://localhost:8080/api/upload" enctype="multipart/form-data" method="POST">

   8:     What is your name?

   9:     <input name="submitter" size="40" type="text"><br>

  10:     What files are you uploading?

  11:     <input name="data" type=file multiple>

  12:     <br>

  13:     <input type="submit" />

  14:     </form>

  15: </body>

  16: </html>

  17: 

    提交数据的原则与HTML4相同,但现在你可以选择多个文件,每个文件以自己的MIME正文部分为结束标志。
创建一个API控制器
    首先,我们创建一个ApiController实现一个HTTP POST行为来处理文件上传。请注意因为我们异步读取文件,所以该操作返回Task<T>。
    注:我们使用了Visual Studio11测试版推出的新的异步/等候关键字,但你同样可以使用已经在Visual Studio 2010中的Tasks和ContinueWith模式。
    我们做的第一件事是检查内容是不是确实的“multipart/ form-data”。 第二件事情,我们要做的是创建一个让您控制内容结束的MultipartFormDataStreamProvider函数。在这种情况下,我们保存文件夹的路径是“c:\tmp\uploads”。 它还包含有关文件存储的信息。
    如果你想完全控制书面文件和一些文件名称,那么你可以从MultipartFormDataStreamProvider进行派生,重写所需的功能和使用StreamProvider函数来代替。
    一旦读操作完成,我们就能检查在完成异步读取内容的同时,任务也随即完成,这时我们产生一个响应,它包含了我们在服务器上使用的提交文件名,显然,这不是一个典型的响应,但是这就是你看到的信息。

   1: public class UploadController : ApiController

   2: {

   3:     public async Task<List<string>> PostMultipartStream()

   4:     {

   5:         // Verify that this is an HTML Form file upload request

   6:         if (!Request.Content.IsMimeMultipartContent("form-data"))

   7:         {

   8:             throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);

   9:         }

  10: 

  11:         // Create a stream provider for setting up output streams that saves the output under c:\tmp\uploads

  12:         // If you want full control over how the stream is saved then derive from MultipartFormDataStreamProvider

  13:         // and override what you need.

  14:         MultipartFormDataStreamProvider streamProvider = new MultipartFormDataStreamProvider("c:\\tmp\\uploads");

  15: 

  16:         // Read the MIME multipart content using the stream provider we just created.

  17:         IEnumerable<HttpContent> bodyparts = await Request.Content.ReadAsMultipartAsync(streamProvider);

  18: 

  19:         // The submitter field is the entity with a Content-Disposition header field with a "name" parameter with value "submitter"

  20:         string submitter;

  21:         if (!bodyparts.TryGetFormFieldValue("submitter", out submitter))

  22:         {

  23:             submitter = "unknown";

  24:         }

  25: 

  26:         // Get a dictionary of local file names from stream provider.

  27:         // The filename parameters provided in Content-Disposition header fields are the keys.

  28:         // The local file names where the files are stored are the values.

  29:         IDictionary<string, string> bodyPartFileNames = streamProvider.BodyPartFileNames;

  30: 

  31:         // Create response containing information about the stored files.

  32:         List<string> result = new List<string>();

  33:         result.Add(submitter);

  34: 

  35:         IEnumerable<string> localFiles = bodyPartFileNames.Select(kv => kv.Value);

  36:         result.AddRange(localFiles);

  37: 

  38:         return result;

  39:     }

  40: }

    在上面的代码中,我们添加了一个可以获得字符串类型的提交者表单字段。这个扩展方法看起来像这下面一样:

   1: public static bool TryGetFormFieldValue(this IEnumerable<HttpContent> contents, string dispositionName, out string formFieldValue)

   2: {

   3:     if (contents == null)

   4:     {

   5:         throw new ArgumentNullException("contents");

   6:     }

   7: 

   8:     HttpContent content = contents.FirstDispositionNameOrDefault(dispositionName);

   9:     if (content != null)

  10:     {

  11:         formFieldValue = content.ReadAsStringAsync().Result;

  12:         return true;

  13:     }

  14: 

  15:     formFieldValue = null;

  16:     return false;

  17: }

托管控制器
    在这个例子中,我们构建一个简单的设置应用程序,那么当在HttpSelfHostConfiguration中配置MaxReceivedMessageSize和TransferMode时,就可以使用托管ApiController。然后,我们用一个简单的HTML表单指向ApiController,这样我们就可以使用浏览器上传文件。这个HTML表单可以被托管在任何地方 - 在这里,我们只是拖放到文件夹C:\inetpub\wwwroot\Samples中,并通过使用因特网信息服务来提供服务。

   1: class Program

   2: {

   3:     static void Main(string[] args)

   4:     {

   5:         var baseAddress = "http://localhost:8080/";

   6:         HttpSelfHostServer server = null;

   7: 

   8:         try

   9:         {

  10:             // Create configuration

  11:             var config = new HttpSelfHostConfiguration(baseAddress);

  12: 

  13:             // Set the max message size to 1M instead of the default size of 64k and also

  14:             // set the transfer mode to 'streamed' so that don't allocate a 1M buffer but

  15:             // rather just have a small read buffer.

  16:             config.MaxReceivedMessageSize = 1024 * 1024;

  17:             config.TransferMode = TransferMode.Streamed;

  18: 

  19:             // Add a route

  20:             config.Routes.MapHttpRoute(

  21:               name: "default",

  22:               routeTemplate: "api/{controller}/{id}",

  23:               defaults: new { controller = "Home", id = RouteParameter.Optional });

  24: 

  25:             server = new HttpSelfHostServer(config);

  26: 

  27:             server.OpenAsync().Wait();

  28: 

  29:             Console.WriteLine("Hit ENTER to exit");

  30:             Console.ReadLine();

  31:         }

  32:         finally

  33:         {

  34:             if (server != null)

  35:             {

  36:                 server.CloseAsync().Wait();

  37:             }

  38:         }

  39:     }

  40: }

    尝试一下启动配置应用程序,然后将你的浏览器指向HTML表单,例子:http://localhost/samples/uploadsample.html

一旦你上传文件成功,你会发现它在文件夹c:\\tmp\uploads中,如果你要求一个JSON,它还会自动内容协商,像:["henrik","c:\\tmp\\uploads\\sample.random"]

原文地址:https://www.cnblogs.com/fx2008/p/2817804.html