ASP.NET Web API에서에서 HTML 양식 데이터 보내기: 파일 업로드 및 다중 파트 MIMESending HTML Form Data in ASP.NET Web API: File Upload and Multipart MIME

Mike Wassonby Mike Wasson

2부: 파일 업로드 및 다중 파트 MIMEPart 2: File Upload and Multipart MIME

이 자습서에는 웹 API에 파일을 업로드 하는 방법을 보여 줍니다.This tutorial shows how to upload files to a web API. 다중 파트 MIME 데이터를 처리 하는 방법을 설명 합니다.It also describes how to process multipart MIME data.

파일을 업로드 하는 것에 대 한 HTML 폼의 예는 다음과 같습니다.Here is an example of an HTML form for uploading a file:

<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>

이 양식에 텍스트 입력된 컨트롤 및 파일 입력된 컨트롤을 포함합니다.This form contains a text input control and a file input control. 폼 파일 입력된 컨트롤을 포함 하는 경우는 enctype 특성은 항상 "다중 파트/폼 데이터", 다중 파트 MIME 메시지를 폼은 보내도록 지정 합니다.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.

예제 요청을 확인 하 여 이해 하기 쉬운 다중 파트 MIME 메시지의 형식은 다음과 같습니다.The format of a multipart MIME message is easiest to understand by looking at an example request:

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.

Note

임의 구성 요소를 포함 하는 부분 경계 ("41184676334") 경계 문자열 메시지 파트 내에서 실수로 표시 되지 않습니다 있도록 합니다.The part boundary includes a random component ("41184676334") to ensure that the boundary string does not accidentally appear inside a message part.

각 메시지 파트 파트 콘텐츠에 뒤에 하나 이상의 헤더를 포함 합니다.Each message part contains one or more headers, followed by the part contents.

  • Content-disposition 헤더 컨트롤의 이름을 포함합니다.The Content-Disposition header includes the name of the control. 또한 파일에 대 한 파일 이름을 포함 합니다.For files, it also contains the file name.
  • Content-type 헤더에에서 데이터를 설명합니다.The Content-Type header describes the data in the part. 이 헤더를 생략 하면 기본값은 텍스트/일반입니다.If this header is omitted, the default is text/plain.

이전 예제에서는 사용자 GrandCanyon.jpg, 콘텐츠 형식이 image/jpeg; 이라는 파일을 업로드 입력 텍스트의 값이 고 "여름 휴가"합니다.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".

파일 업로드File Upload

이제는 다중 파트 MIME 메시지에서 파일을 읽는 Web API 컨트롤러에 살펴보겠습니다.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를 사용 하 여 비동기 작업을 지원 합니다 작업 기반 프로그래밍 모델합니다.Web API supports asynchronous actions using the task-based programming model. 먼저, 다음은 코드를 지 원하는.NET Framework 4.5를 대상으로 하는 경우는 비동기 하 고 await 키워드입니다.First, here is the code if you are targeting .NET Framework 4.5, which supports the async and await keywords.

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.
        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.

합니다 IsMultipartContent 메서드는 다중 파트 MIME 메시지를 요청에 포함 되는지 여부를 확인 합니다.The IsMultipartContent method checks whether the request contains a multipart MIME message. 그렇지 않은 경우 컨트롤러 HTTP 상태 코드 415 (지원 되지 않는 미디어 형식)를 반환 합니다.If not, the controller returns HTTP status code 415 (Unsupported Media Type).

합니다 MultipartFormDataStreamProvider 클래스는 파일 스트림을 업로드 된 파일에 대 한 할당 하는 도우미 개체입니다.The MultipartFormDataStreamProvider class is a helper object that allocates file streams for uploaded files. 다중 파트 MIME 메시지를 읽으려면 호출을 ReadAsMultipartAsync 메서드.To read the multipart MIME message, call the ReadAsMultipartAsync method. 이 메서드는 모든 메시지 파트를 추출 하 고 제공한 스트림으로 씁니다 합니다 MultipartFormDataStreamProvider합니다.This method extracts all of the message parts and writes them into the streams provided by the MultipartFormDataStreamProvider.

메서드가 완료 되 면 파일에 대 한 정보를 가져올 수 있습니다 합니다 데이터 컬렉션인 속성의 MultipartFileData 개체입니다.When the method completes, you can get information about the files from the FileData property, which is a collection of MultipartFileData objects.

  • MultipartFileData.FileName 저장 된 파일 서버의 로컬 파일 이름입니다.MultipartFileData.FileName is the local file name on the server, where the file was saved.
  • MultipartFileData.Headers 파트 헤더가 포함 되어 있습니다 (하지 요청 헤더).MultipartFileData.Headers contains the part header (not the request header). 콘텐츠에 액세스 하는 데 사용할 수 있습니다_기질 및 Content-type 헤더입니다.You can use this to access the Content_Disposition and Content-Type headers.

이름에서 알 수 있듯이 ReadAsMultipartAsync 비동기 메서드입니다.As the name suggests, ReadAsMultipartAsync is an asynchronous method. 메서드가 완료 된 후 작업을 수행 하려면 사용 된 연속 작업이 (.NET 4.0) 또는 await 키워드 (.NET 4.5).To perform work after the method completes, use a continuation task (.NET 4.0) or the await keyword (.NET 4.5).

.NET Framework 4.0 버전 이전 코드는 다음과 같습니다.Here is the .NET Framework 4.0 version of the previous code:

public Task<HttpResponseMessage> PostFormData()
{
    // Check if the request contains 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.
    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

앞서 있는 HTML 폼에 텍스트 입력된 컨트롤을 했습니다.The HTML form that I showed earlier had a text input control.

<div>
        <label for="caption">Image Caption</label>
        <input name="caption" type="text" />
    </div>

컨트롤의 값을 가져올 수는 FormData 의 속성을 MultipartFormDataStreamProvider합니다.You can get the value of the control from the FormData property of the MultipartFormDataStreamProvider.

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 되는 NameValueCollection 폼 컨트롤에 대 한 이름/값 쌍이 포함 된 합니다.FormData is a NameValueCollection that contains name/value pairs for the form controls. 컬렉션 중복 키를 포함할 수 있습니다.The collection can contain duplicate keys. 이 양식을 고려해 보세요.Consider this form:

<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>

요청 본문은 다음과 같습니다.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--

이런 경우는 FormData 컬렉션에는 다음 키/값 쌍을 포함 됩니다.In that case, the FormData collection would contain the following key/value pairs:

  • 여정: 라운드트립trip: round-trip
  • options: nonstopoptions: nonstop
  • 옵션: 날짜options: dates
  • 사용자: 창seat: window