Freigeben über


Senden von HTML-Formulardaten in ASP.NET-Web-API: Dateiupload und mehrteiliger MIME

Teil 2: Dateiupload und mehrteiliger MIME

In diesem Tutorial erfahren Sie, wie Sie Dateien in eine Web-API hochladen. Außerdem wird beschrieben, wie mehrteilige MIME-Daten verarbeitet werden.

Hier sehen Sie ein Beispiel für ein HTML-Formular zum Hochladen einer Datei:

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

Screenshot eines HTML-Formulars mit einem Bildbeschriftungsfeld mit dem Text Sommerurlaub und einer Bilddatei-Dateiauswahl.

Dieses Formular enthält ein Texteingabesteuerelement und ein Dateieingabesteuerelement. Wenn ein Formular ein Dateieingabesteuerelement enthält, sollte das enctype-Attribut immer "multipart/form-data" sein, was angibt, dass das Formular als mehrteilige MIME-Nachricht gesendet wird.

Das Format einer mehrteiligen MIME-Nachricht ist am einfachsten zu verstehen, indem Sie sich eine Beispielanforderung ansehen:

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

Diese Meldung ist in zwei Teile unterteilt, einen für jedes Formularsteuerelement. Teilgrenzen werden durch die Linien gekennzeichnet, die mit Bindestrichen beginnen.

Hinweis

Die Teilgrenze enthält eine zufällige Komponente ("41184676334"), um sicherzustellen, dass die Begrenzungszeichenfolge nicht versehentlich in einem Nachrichtenteil angezeigt wird.

Jeder Nachrichtenteil enthält eine oder mehrere Header, gefolgt vom Teilinhalt.

  • Der Content-Disposition-Header enthält den Namen des Steuerelements. Für Dateien enthält es auch den Dateinamen.
  • Der Content-Type-Header beschreibt die Daten im Teil. Wenn dieser Header ausgelassen wird, ist der Standardwert text/plain.

Im vorherigen Beispiel hat der Benutzer eine Datei mit dem Namen GrandCanyon.jpg mit dem Inhaltstyp image/jpeg hochgeladen. und der Wert der Texteingabe lautete "Sommerurlaub".

Dateiupload

Sehen wir uns nun einen Web-API-Controller an, der Dateien aus einer mehrteiligen MIME-Nachricht liest. Der Controller liest die Dateien asynchron. Die Web-API unterstützt asynchrone Aktionen mithilfe des aufgabenbasierten Programmiermodells. Hier sehen Sie zunächst den Code, wenn Sie .NET Framework 4.5 als Ziel verwenden, der die Schlüsselwörter async und await unterstützt.

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

}

Beachten Sie, dass die Controlleraktion keine Parameter annimmt. Das liegt daran, dass wir den Anforderungstext innerhalb der Aktion verarbeiten, ohne einen Medientypformatierer aufzufordern.

Die IsMultipartContent-Methode überprüft, ob die Anforderung eine mehrteilige MIME-Nachricht enthält. Andernfalls gibt der Controller HTTP-status Code 415 (nicht unterstützter Medientyp) zurück.

Die MultipartFormDataStreamProvider-Klasse ist ein Hilfsobjekt, das Dateistreams für hochgeladene Dateien zuordnet. Um die mehrteilige MIME-Nachricht zu lesen, rufen Sie die ReadAsMultipartAsync-Methode auf. Diese Methode extrahiert alle Nachrichtenteile und schreibt sie in die Datenströme, die vom MultipartFormDataStreamProvider bereitgestellt werden.

Wenn die Methode abgeschlossen ist, können Sie Informationen zu den Dateien aus der FileData-Eigenschaft abrufen, bei der es sich um eine Sammlung von MultipartFileData-Objekten handelt.

  • MultipartFileData.FileName ist der lokale Dateiname auf dem Server, auf dem die Datei gespeichert wurde.
  • MultipartFileData.Headers enthält den Teilheader (nicht den Anforderungsheader). Sie können damit auf die Header Content_Disposition und Content-Type zugreifen.

Wie der Name schon sagt, ist ReadAsMultipartAsync eine asynchrone Methode. Verwenden Sie zum Ausführen der Arbeit nach Abschluss der Methode einen Fortsetzungstask (.NET 4.0) oder den await Schlüsselwort (keyword) (.NET 4.5).

Dies ist die .NET Framework Version 4.0 des vorherigen Codes:

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

Lesen von Formularsteuerelementdaten

Das HTML-Formular, das ich zuvor gezeigt habe, verfügte über ein Texteingabesteuerelement.

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

Sie können den Wert des Steuerelements aus der FormData-Eigenschaft des MultipartFormDataStreamProvider abrufen.

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 ist eine NameValueCollection , die Name/Wert-Paare für die Formularsteuerelemente enthält. Die Auflistung kann doppelte Schlüssel enthalten. Betrachten Sie dieses Formular:

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

Screenshot des HTML-Formulars mit ausgefülltem Round-Trip Kreis und aktivierten Kontrollkästchen Nur Nicht-Stopp-Flüge anzeigen und Meine Reisedaten sind flexibel.

Der Anforderungstext kann wie folgt aussehen:

-----------------------------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 diesem Fall würde die FormData-Auflistung die folgenden Schlüssel-Wert-Paare enthalten:

  • Reise: Hin- und Rückflug
  • Optionen: pausenlos
  • Optionen: Datumsangaben
  • Sitz: Fenster