ASP.NET MVC – Upload

written by devangelist on August 4, 2012 in ASP.NET and ASP.NET MVC and Javascript and jQuery with no comments

Sowas wie ein klassisches Fileupload-Control (oder Html-Helper) gibt es in ASP.NET MVC nicht. Braucht man auch nicht.

<form id="myForm" method="post" enctype="multipart/form-data">
    <input id="FileInput" type="file" name="FileInput" />
</form>

Auf dem Server bzw. im Controller kann anschließend über Request.Files["FileInput"] auf die Datei zugegriffen werden. Wichtig ist das enctype ”multipart/form-data”, denn somit wird der Inhalt einer jeden Datei in eine getrennte Sektion des multiparts Dokumentes gepackt.

Allerdings sind klassische Uploadfelder Sache von gestern, heute ist Multiupload Standard bzw. die Drag und Drop Variante aus dem Windows Fileexplorer auch keine Seltenheit.

Ein Multiupload kann nicht direkt in HTML gemacht werden, dafür ist ein Plugin wie beispielsweise Flash (swfupload) oder Silverlight erforderlich, denn nur so können mehrere Datei in einem Schritt ausgewählt werden. Ein einfaches jQuery Plugin (uploadify) erspart den Kampf mit Flash und benötigt nur die Einbindung einer JS und CSS Datei sowie ein input im HTML-Markup der Seite:

<input id="FileInput" type="file" name="FileInput" />

Das Plugin wird im Head-Bereich der Seite erstellt: (Mehr Infos in der Doku)

$('#FileInput').uploadify({
    'uploader'  : '/Content/swf/uploadify.swf', // das swf objekt
    'script'    : '/Home/Upload', // Die Controller Action
    'cancelImg' : '/Content/Images/cancel.png',
    'folder'    : '/Uploads',
    'auto'      : true
});

Der Controller

public string Upload(HttpPostedFileBase fileData)
{
    var fileName = Server.MapPath("~/uploads/" + System.IO.Path.GetFileName(FileData.FileName));
    FileData.SaveAs(fileName);

    return "ok";
}

Ein return “ok” versteht sich selbstverständlich nur als Beispiel, da kann schon etwas mehr Eleganz eingebaut werden.
Wichtig an der Stelle, der Parameter muss als “fileData” benannt werden und vom Typ HttpPostedFileBase und nicht HttpPostedFile sein.

Aber dann…

Es sollte ja natürlich nicht jeder Dateien hochladen sondern nur autorisierte Benutzer, was mit einem [Authorize]-Attribut gelöst wird.

[Authorize]
public string Upload(HttpPostedFileBase fileData)
{
    ...
}

Das wäre jetzt etwas zu einfach gewesen. Durch das Hinzufügen des Attributes meldet das Plugin einen Fehler.
Durch eine Suche im Internet wird schnell klar, dass es sich um einen Bug in Flash handelt der zur Folge führt, dass die ASP.NET Session und Authentifizierungs-Cookies nicht mitgesendet werden. Zum Glück gibt es im uploadify jede Menge Einstellungsmöglichkeiten und mit dem scriptData Parameter können manuell das Authentifizierungstoken und die ASP.NET SessionId mitgegeben werden. Dazu werden das Token sowie die Session erstmal im client benötigt:

var token = "@(Request.Cookies[FormsAuthentication.FormsCookieName]==null ? string.Empty : Request.Cookies[FormsAuthentication.FormsCookieName].Value)";
var sessionId = "@Session.SessionID";

$('#FileInputWithAuth').uploadify({
    'scriptData': { SessionId: sessionId, Token: token }
    ...
});

Es gibt zwei Möglichkeiten, serverseitig die Parameter zu verarbeiten bzw. in die Auth- Logik einzubauen.

Möglichkeit 1) Die SessionId und das Authentifizierungscookie in der Global.asax wiedererstellen, Änderung in der Global.asax Datei.

In der Begin Request methode folgenden code unterbringen:

protected void Application_BeginRequest(object sender, EventArgs e)
{
    try
    {
        const string sessionParamName = "SessionId";
        const string sessionCookieName = "ASP.NET_SessionId";

        if (HttpContext.Current.Request.Form[sessionParamName] != null)
        {
            UpdateCookie(sessionCookieName, HttpContext.Current.Request.Form[sessionParamName]);
        }
        else if (HttpContext.Current.Request.QueryString[sessionParamName] != null)
        {
            UpdateCookie(sessionCookieName, HttpContext.Current.Request.QueryString[sessionParamName]);
        }
    }
    catch
    {
    }

    try
    {
        const string authParamName = "Token";
        string authCookieName = FormsAuthentication.FormsCookieName;

        if (HttpContext.Current.Request.Form[authParamName] != null)
        {
            UpdateCookie(authCookieName, HttpContext.Current.Request.Form[authParamName]);
        }
        else if (HttpContext.Current.Request.QueryString[authParamName] != null)
        {
            UpdateCookie(authCookieName, HttpContext.Current.Request.QueryString[authParamName]);
        }
    }
    catch
    {
    }
}

private static void UpdateCookie(string cookieName, string cookieValue)
{
    var cookie = HttpContext.Current.Request.Cookies.Get(cookieName) ?? new HttpCookie(cookieName);
    cookie.Value = cookieValue;
    HttpContext.Current.Request.Cookies.Set(cookie);
}

Möglichkeit B) Ein eigenes Authorize Attribut, das ebenfalls das Cookie ausliest und validiert.

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    private const string TokenKey = "token";

    protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
    {
        var token = httpContext.Request.Params[TokenKey];

        if (token != null)
        {
            FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(token);

            if (ticket != null)
            {
                var identity = new FormsIdentity(ticket);
                var principal = ...
            }
        }

        return base.AuthorizeCore(httpContext);
    }
}

Beide Möglichkeiten führen zum gewünschten Effekt, einzig die Auslagerung, welche im Attribut etwas eleganter gelöst ist.

ASP.NET MVC – Upload: 1 Stern2 Sterne3 Sterne4 Sterne5 Sterne 4,17 von 5 Punkte, 6 abgegebene Stimmen.
Loading ... Loading ...

About the Author

Roberto Bez ist passionierter Webentwickler und TechLead bei der HolidayCheck AG. Für Roberto bedeutet das Entwickeln nicht nur Arbeit, sondern auch Freude, Motivation und täglich neue, aufregende Herausforderungen. Besonders gerne setzt er sich mit neuen Webtechnologien sowie Datenbanken aller Art auseinander und versucht diese in die tägliche Anwendungsentwicklung miteinzubringen. Neben dem Entwickeln trifft man ihn gerne Abends beim Laufen oder im Sommer bei Mountainbike-Touren durch die schönen Berge Südtirols.