ASP.NET Web API'sinde HTML Form Verileri Gönderme: Dosya Yükleme ve Çok Bölümlü MIME

Bölüm 2: Dosya Yükleme ve Çok Parçalı MIME

Bu öğreticide bir web API'sine nasıl dosya yüklendiği gösterilir. Ayrıca çok parçalı MIME verilerinin nasıl işlendiği de açıklanır.

Aşağıda, dosyayı karşıya yüklemek için bir HTML formu örneği verilmiştir:

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

Yaz Tatili ve Resim Dosyası dosya seçici metnini içeren Resim Yazısı alanını gösteren HTML formunun ekran görüntüsü.

Bu form bir metin girişi denetimi ve bir dosya girişi denetimi içerir. Form bir dosya giriş denetimi içerdiğinde, enctype özniteliği her zaman formun çok parçalı bir MIME iletisi olarak gönderileceğini belirten "çok parçalı/form-veri" olmalıdır.

Çok parçalı BIR MIME iletisinin biçimini anlamak için örnek bir isteğe göz atabilirsiniz:

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

Bu ileti, her form denetimi için bir tane olmak üzere iki bölüme ayrılır. Parça sınırları tirelerle başlayan çizgilerle gösterilir.

Not

Parça sınırı, sınır dizesinin yanlışlıkla ileti parçası içinde görünmemesini sağlamak için rastgele bir bileşen ("41184676334") içerir.

Her ileti bölümü bir veya daha fazla üst bilgi ve ardından bölüm içeriği içerir.

  • Content-Disposition üst bilgisi denetimin adını içerir. Dosyalar için dosya adını da içerir.
  • content-type üst bilgisi bölümdeki verileri açıklar. Bu üst bilgi atlanırsa, varsayılan değer metin/düz olur.

Önceki örnekte kullanıcı, image/jpeg içerik türüne sahip GrandCanyon.jpg adlı bir dosyayı karşıya yükledi; ve metin girişinin değeri "Yaz Tatili" idi.

Dosya Karşıya Yükleme

Şimdi çok parçalı bir MIME iletisindeki dosyaları okuyan bir Web API denetleyicisine bakalım. Denetleyici dosyaları zaman uyumsuz olarak okur. Web API'si , görev tabanlı programlama modelini kullanarak zaman uyumsuz eylemleri destekler. İlk olarak, async ve await anahtar sözcüklerini destekleyen .NET Framework 4.5'i hedefliyorsanız kod aşağıdadır.

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);
        }
    }

}

Denetleyici eyleminin herhangi bir parametre almadığını göreceksiniz. Bunun nedeni, bir medya türü biçimlendirici çağırmadan eylemin içindeki istek gövdesini işlememizdir.

IsMultipartContent yöntemi, isteğin çok parçalı bir MIME iletisi içerip içermediğini denetler. Aksi takdirde denetleyici HTTP durum kodu 415 'i (Desteklenmeyen Medya Türü) döndürür.

MultipartFormDataStreamProvider sınıfı, karşıya yüklenen dosyalar için dosya akışlarını ayıran bir yardımcı nesnedir. Çok parçalı MIME iletisini okumak için ReadAsMultipartAsync yöntemini çağırın. Bu yöntem tüm ileti bölümlerini ayıklar ve MultipartFormDataStreamProvider tarafından sağlanan akışlara yazar.

Yöntem tamamlandığında, MultipartFileData nesnelerinin bir koleksiyonu olan FileData özelliğinden dosyalar hakkında bilgi alabilirsiniz.

  • MultipartFileData.FileName , dosyanın kaydedildiği sunucudaki yerel dosya adıdır.
  • MultipartFileData.Headers , parça üst bilgisini (istek üst bilgisini değil ) içerir. Content_Disposition ve İçerik Türü üst bilgilerine erişmek için bunu kullanabilirsiniz.

Adından da anlaşılacağı gibi ReadAsMultipartAsync zaman uyumsuz bir yöntemdir. Yöntem tamamlandıktan sonra çalışma gerçekleştirmek için bir devamlılık görevi (.NET 4.0) veya await anahtar sözcüğünü (.NET 4.5) kullanın.

Önceki kodun .NET Framework 4.0 sürümü aşağıdadır:

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

Form Denetim Verilerini Okuma

Daha önce gösterdiğim HTML formunda metin girişi denetimi vardı.

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

Denetimin değerini MultipartFormDataStreamProvider'ınFormData özelliğinden alabilirsiniz.

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 , form denetimleri için ad/değer çiftleri içeren bir NameValueCollection'dır . Koleksiyon yinelenen anahtarlar içerebilir. Şu formu göz önünde bulundurun:

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

Round-Trip dairenin doldurulduğu HTML formunun ekran görüntüsü ve Yalnızca durdurulmayan uçuşları göster ve Seyahat tarihlerim esnek kutuları işaretli.

İstek gövdesi şöyle görünebilir:

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

Bu durumda , FormData koleksiyonu aşağıdaki anahtar/değer çiftlerini içerir:

  • seyahat: gidiş dönüş
  • seçenekler: kesintisiz
  • seçenekler: tarihler
  • koltuk: pencere