5.3 Sending HTML Form Data
5.3 發送HTML表單數據(2)
本文引自:http://www.cnblogs.com/r01cn/archive/2012/12/20/2826230.html
By Mike Wasson|June 21, 2012
作者:Mike Wasson | 日期:2012-6-21
Part 2: File Upload and Multipart MIME
第2部分:文件上傳與多部分MIME
This tutorial shows how to upload files to a web API. It also describes how to process multipart MIME data.
本教程演示如何對Web API上傳文件。也描述如何處理多部分MIME數據。
Here is an example of an HTML form for uploading a file:
以下是一個上傳文件的HTML表單(如圖5-8):
<form name="form1" method="post" enctype="multipart/form-data" action="api/upload"> <div> <label for="caption">Image Caption</label> <input name="caption" type="text" /> </div> <div> <label for="image1">Image File</label> <input name="image1" 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 http://localhost: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; name="caption"
Summer vacation -----------------------------41184676334 Content-Disposition: form-data; name="image1"; 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的代碼,它支持async和await關鍵字。
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); } } }
Notice that the controller action does not take any parameters. That's because we process the request body inside the action, without invoking a media-type formatter.
注意,控制器動作不取任何參數。這是因為我們是在動作中處理請求體的,不必調用media-type格式化器。
The IsMultipartContent method checks whether the request contains a multipart MIME message. If not, the controller returns HTTP status code 415 (Unsupported Media Type).
IsMultipartContent方法檢查該請求是否含有多部分MIME消息。如果不是,控制器返回HTTP狀態碼415(不支持的媒體類型)。
The MultipartFormDataStreamProvider class is a helper object that allocates file streams for uploaded files. To read the multipart MIME message, call the ReadAsMultipartAsync method. This method extracts all of the message parts and writes them into the streams provided by the MultipartFormDataStreamProvider.
MultipartFormDataStreamProvider類是一個輔助器對象,它為上傳文件分配文件流。為了讀取多部分MIME消息,需調用ReadAsMultipartAsync方法。該方法提取所有消息部件,並把它們寫入由MultipartFormDataStreamProvider提供的流中。
When the method completes, you can get information about the files from the FileData property, which is a collection of MultipartFileDataobjects.
當該方法完成時,你可以通過FileData屬性獲得文件的信息,該屬性是一個MultipartFileData對象的集合。
- MultipartFileData.FileName is the local file name on the server, where the file was saved.
MultipartFileData.FileName是保存此文件的服務器上的一個本地文件名。 - MultipartFileData.Headers contains the part header (not the request header). You can use this to access the Content_Disposition and Content-Type headers.
MultipartFileData.Headers含有部件報頭(不是請求報頭)。你可以用它來訪問Content_Disposition和Content-Type報頭。
As the name suggests, ReadAsMultipartAsync is an asynchronous method. To perform work after the method completes, use a continuation task (.NET 4.0) or the await keyword (.NET 4.5).
正如名稱所暗示的那樣,ReadAsMultipartAsync是一個異步方法。為了在該方法完成之后執行一些工作,需要使用“continuation(繼續)任務”(.NET 4.0)或await關鍵字(.NET 4.5)。
Here is the .NET Framework 4.0 version of the previous code:
以下是前述代碼的.NET Framework 4.0版本:
public 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);
// Read the form data and return an async task. // 讀取表單數據,並返回一個async任務 var task = Request.Content.ReadAsMultipartAsync(provider). ContinueWith<HttpResponseMessage>(t => { if (t.IsFaulted || t.IsCanceled) { Request.CreateErrorResponse(HttpStatusCode.InternalServerError, t.Exception); }
// 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); });
return task; }
Reading Form Control Data
讀取表單控件數據
The HTML form that I showed earlier had a text input control.
前面顯示的HTML表單有一個文本輸入控件。
<div> <label for="caption">Image Caption</label> <input name="caption" type="text" /> </div>
You can get the value of the control from the FormData property of the MultipartFormDataStreamProvider.
可以通過MultipartFormDataStreamProvider的FormData屬性獲取控件的值。
public async Task<HttpResponseMessage> PostFormData() { if (!Request.Content.IsMimeMultipartContent()) { throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType); }
string root = HttpContext.Current.Server.MapPath("~/App_Data"); var provider = new MultipartFormDataStreamProvider(root);
try { await Request.Content.ReadAsMultipartAsync(provider);
// Show all the key-value pairs. // 顯示所有“鍵-值”對 foreach (var key in provider.FormData.AllKeys) { foreach (var val in provider.FormData.GetValues(key)) { Trace.WriteLine(string.Format("{0}: {1}", key, val)); } }
return Request.CreateResponse(HttpStatusCode.OK); } catch (System.Exception e) { return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e); } }
FormData is a NameValueCollection that contains name/value pairs for the form controls. The collection can contain duplicate keys. Consider this form:
FormData是一個NameValueCollection(名字/值集合),它含有表單控件的“名字/值”對。該集合可能含有重復鍵。考慮以下表單(如圖5-9):
<form name="trip_search" method="post" enctype="multipart/form-data" action="api/upload"> <div> <input type="radio" name="trip" value="round-trip"/> Round-Trip </div> <div> <input type="radio" name="trip" value="one-way"/> One-Way </div>
<div> <input type="checkbox" name="options" value="nonstop" /> Only show non-stop flights </div> <div> <input type="checkbox" name="options" value="airports" /> Compare nearby airports </div> <div> <input type="checkbox" name="options" value="dates" /> My travel dates are flexible </div>
<div> <label for="seat">Seating Preference</label> <select name="seat"> <option value="aisle">Aisle</option> <option value="window">Window</option> <option value="center">Center</option> <option value="none">No Preference</option> </select> </div> </form>

圖5-9. 有重復鍵的表單
The request body might look like this:
該請求體看上去可能像這樣:
-----------------------------7dc1d13623304d6 Content-Disposition: form-data; name="trip"
round-trip -----------------------------7dc1d13623304d6 Content-Disposition: form-data; name="options"
nonstop -----------------------------7dc1d13623304d6 Content-Disposition: form-data; name="options"
dates -----------------------------7dc1d13623304d6 Content-Disposition: form-data; name="seat"
window -----------------------------7dc1d13623304d6--
In that case, the FormData collection would contain the following key/value pairs:
在這種情況下,FormData集合會含有以下“鍵/值”對:
- trip: round-trip
- options: nonstop
- options: dates
- seat: window