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”。
文件上传
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);
}
}
}