FileUpload mit AngularJS und ASP.NET MVC mit HttpPostedFileBase


Leider gibt es in AngularJS keine nativ integrierte Direktive für den Upload von Dateien. Es existieren aber diverse Möglichkeiten auch mit AngularJS Dateien hochzuladen. Zum einen gibt es zahlreiche fertige Upload Direktiven von Community Mitgliedern

Wenn man nur einen simplen Upload von Dateien umsetzen möchte, kann man das ganze auch recht schnell selbst schreiben. Dazu gibt es ebenfalls genug Hilfe im Netz und ich habe mich im Großen und Ganzen an die folgende Anleitung gehalten:

http://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs

Das ganze dann mit ASP.NET MVC im Backend kann folgendermaßen aussehen.

Als erstes benötigen wir eine Funktion im Controller die unsere Datei entgegennimmt. Ich sende außerdem mit den Dateien noch ein Personen Objekt mit. Hier ist es mit leider nicht gelungen, das Personen Objekt vom MVC Modelbinder entsprechend erkannt und gefüllt wird. Als die Upload Funktion noch die Struktur

“public JsonResult Upload(HttpPostedFileBase file, UploadPerson person)” hatte,

war “person” immer null, hier freue ich mich gerne über einen entsprechenden Hinweis wie man das lösen kann, denn ich habe hier spontan keine Lösung gefunden. Daher parse ich das Person Objekt aktuell per “Hand” über den JavaScriptSearializer, denn in den Formdaten werden die Daten entsprechend mit übergeben.

[HttpPost]
public JsonResult Upload(HttpPostedFileBase file)
{
    if (file != null && file.ContentLength > 0)
    {
        //Da ich aktuell nicht weiß wie ich ebenfalls z.B. "UploadPerson" direkt im Upload mit
        //"befüllen" lassen kann füllen wir dieses Model erst einmal per "Hand".
        var json = HttpContext.Request.Form["person"];
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        UploadPerson person = (UploadPerson)serializer.Deserialize(json, typeof(UploadPerson));
        //TODO etwas mit den Personendaten machen.

        var fileName = Path.GetFileName(file.FileName);
        var path = Path.Combine(Server.MapPath("~/Images/"), fileName);
        file.SaveAs(path);
    }

    return Json(true, JsonRequestBehavior.AllowGet);
}
public class UploadPerson
{
    public string Name { get; set; }

    public string Email { get; set; }
}

Nach dem auf der .NET Seite alle Funktionen und Klassen umgesetzt sind, fehlt nur noch der HTML und JavaScript Part.

Im HTML Definieren wir unsere Eingabefelder wie FileUpload und Textfelder für unser Person Objekt

<div ng-app="uploadApp" ng-controller="myCtrl">
    <div class="row">
        <div class="col-md-12">
            <h3>Datei Upload mit AngularJS</h3>
        </div>
        <hr />
    </div>
    <div class="row form-horizontal">
        <div class="form-group">
            <label class="col-sm-2 control-label">File Upload 1</label>
            <div class="col-sm-10">
                <input class="form-control" type="file" file-model="ViewModel.myFile" />
            </div>
        </div>
        <div class="form-group">
            <label class="col-sm-2 control-label">Name</label>
            <div class="col-sm-10">
                <input class="form-control" type="text" ng-model="ViewModel.Person.Name" />
            </div>
        </div>
        <div class="form-group">
            <label class="col-sm-2 control-label">Email</label>
            <div class="col-sm-10">
                <input class="form-control" type="email" ng-model="ViewModel.Person.Email" />
            </div>
        </div>
    </div>
    <div class="row">
        <br />
        <div class="col-md-2">
            <button ng-click="uploadFile()" class="btn btn-default">Datei hochladen</button>
        </div>
    </div>
</div>

Dann fehlt nur noch der passende JavaScript Code.

<script type="text/javascript">
    var app = angular.module("uploadApp", []);
    app.controller("myCtrl", function ($scope, fileUpload) {
        $scope.ViewModel = {};
        $scope.ViewModel.myFile = [];
        $scope.ViewModel.Person = {};
        $scope.ViewModel.Person.Name = "SquadWuschel";
        $scope.ViewModel.Person.Email = "SquadWuschel@@Test.de";
        $scope.uploadFile = function () {
            fileUpload.uploadFileToUrl($scope.ViewModel.myFile, $scope.ViewModel.Person, "Upload");
        }
    });

    app.directive('fileModel', [
        '$parse', function ($parse) {
            return {
                restrict: 'A',
                link: function (scope, element, attrs) {
                    var model = $parse(attrs.fileModel);
                    var modelSetter = model.assign;
                    element.bind('change', function () {
                        scope.$apply(function () {
                            modelSetter(scope, element[0].files[0]);
                        });
                    });
                }
            };
        }
    ]);

    app.service('fileUpload', [
        '$http', '$log', function ($http, $log) {
            this.uploadFileToUrl = function (file, personData, uploadUrl) {
                var formData = new FormData();
                //Unsere Datei hinzufügen
                formData.append('file', file);
                //Unsere Personendaten ebenfalls den Formdaten hinzufügen.
                formData.append('person', angular.toJson(personData)); 
                $http.post(uploadUrl, formData, {
                    transformRequest: angular.identity,
                    headers: { 'Content-Type': undefined }
                })
                    .success(function (data) {
                        $log.log("Upload war erfolgreich");
                    }).error(function () {
                        $log.log("Fehler beim Upload der Daten");
                    });
            };
        }
    ]);
</script>

Im Code ist eine Direktive “fileModel” enthalten die das Äquivalent zum “ngModel” für unser Input vom Typ “file” darstellt. In meinem Beispiel wird also in der Variable “ViewModel.myFile” die entsprechende Datei hinterlegt die ausgewählt wurde.

Im Service werden dann alle Formulardaten an den Server gesendet. Ich füge den Formdaten die Datei aus dem Upload Element hinzu und dann noch die Personendaten.

Das Beispiel findet Ihr auch bei mir auf Codeplex.

Advertisements

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s