WebApi发送HTML表单数据:文件上传与多部分MIME

时间:2022-05-14 07:37:44

This tutorial shows how to upload files to a web API. It also describes how to process multipart MIME data.
本教程演示如何对Web API上传文件。也描述如何处理多部分MIME数据。

Download the completed project.
下载完整的项目。

Here is an example of an HTML form for uploading a file:
以下是一个上传文件的HTML表单(如图5-8):

<form method="post" enctype="multipart/form-data" action="api/upload"> <div> <label for="caption">Image Caption</label> <input type="text" /> </div> <div> <label for="image1">Image File</label> <input type="file" /> </div> <div> <input type="submit" value="Submit" /> </div> </form>

图5-8. 文件上传表单

This form contains a text input control and a file input control. When a form contains a file input control, the enctype attribute should always be "multipart/form-data", which specifies that the form will be sent as a multipart MIME message.
该表单有一个文本输入控件和一个文件输入控件。当表单含有文件输入控件时,其enctype标签属性应当总是“multipart/form-data”,它指示此表单将作为多部分MIME消息进行发送。

The format of a multipart MIME message is easiest to understand by looking at an example request:
通过考察一个示例请求,很容易理解multipart(多部分)MIME消息的格式:

POST :50460/api/values/1 HTTP/1.1 User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:12.0) Gecko/20100101 Firefox/12.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip, deflate Content-Type: multipart/form-data; boundary=---------------------------41184676334 Content-Length: 29278
-----------------------------41184676334 Content-Disposition: form-data;
Summer vacation -----------------------------41184676334 Content-Disposition: form-data;; filename="GrandCanyon.jpg" Content-Type: image/jpeg
(Binary data not shown)(二进制数据,未列出) -----------------------------41184676334--

This message is divided into two parts, one for each form control. Part boundaries are indicated by the lines that start with dashes.
这条消息分成两个部件(parts),分别用于每个表单控件。部件边界由以一些破折号开始的行指示。

The part boundary includes a random component ("41184676334") to ensure that the boundary string does not accidentally appear inside a message part.
部件边界包含了一个随机组件(“41184676334”),以确保边界字符串不会偶然出现在消息部件之中。

Each message part contains one or more headers, followed by the part contents.
每一个消息部件含有一个或多个报头,后跟部件内容。

The Content-Disposition header includes the name of the control. For files, it also contains the file name.
Content-Disposition(内容布置)报头包括控件名称。对于文件,它也包括文件名。

The Content-Type header describes the data in the part. If this header is omitted, the default is text/plain.
Content-Type(内容类型)报头描述部件中的数据。如果该报头被忽略,默认为text/plain(文本格式)。

In the previous example, the user uploaded a file named GrandCanyon.jpg, with content type image/jpeg; and the value of the text input was "Summer Vacation".
在上一示例中,用户上传名为GrandCanyon.jpg的文件,其内容类型为image/jpeg,,而文本输入框的值为“Summer Vacation”。

File Upload
文件上传

Now let‘s look at a Web API controller that reads files from a multipart MIME message. The controller will read the files asynchronously. Web API supports asynchronous actions using the task-based programming model. First, here is the code if you are targeting .NET Framework 4.5, which supports the async and await keywords.
现在,让我们考查从一个多部分MIME消息读取文件的控制器。该控制器将异步读取文件。Web API支持使用“基于任务的编程模型(这是关于.NET并行编程的MSDN文档 — 译者注)”的异步动作。首先,以下是针对.NET Framework 4.5的代码,它支持asyncawait关键字。

using System.Diagnostics; using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.Web; using System.Web.Http;
public class UploadController : ApiController { public async Task<HttpResponseMessage> PostFormData() { // Check if the request contains multipart/form-data. // 检查该请求是否含有multipart/form-data if (!Request.Content.IsMimeMultipartContent()) { throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); }
string root = HttpContext.Current.Server.MapPath("~/App_Data"); var provider = new MultipartFormDataStreamProvider(root);
try { // Read the form data. // 读取表单数据 await Request.Content.ReadAsMultipartAsync(provider);
// This illustrates how to get the file names. // 以下描述如何获取文件名 foreach (MultipartFileData file in provider.FileData) { Trace.WriteLine(file.Headers.ContentDisposition.FileName); Trace.WriteLine("Server file path: " + file.LocalFileName); } return Request.CreateResponse(HttpStatusCode.OK); } catch (System.Exception e) { return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e); } } }