web-dev-qa-db-fra.com

AngularJS: comment implémenter un simple téléchargement de fichier avec un formulaire multipart?

Je souhaite créer un simple formulaire multipart à partir de AngularJS sur un serveur node.js. Le formulaire doit contenir un objet JSON dans une partie et une image dans l'autre partie (je ne poste actuellement que l'objet JSON avec $ resource).

J'ai pensé que je devrais commencer par input type = "file", mais j'ai ensuite découvert que AngularJS ne peut pas se lier à cela ..

tous les exemples que je peux trouver sont pour emballer des plugins jQuery pour drag & drop. Je veux un simple téléchargement d'un fichier.

Je suis nouveau dans AngularJS et ne me sens pas du tout en mesure de rédiger mes propres directives.

140
Gal Ben-Haim

Une vraie solution de travail sans autres dépendances que angularjs (testé avec v.1.0.6)

html

<input type="file" name="file" onchange="angular.element(this).scope().uploadFile(this.files)"/>

Angularjs (1.0.6) ne supporte pas ng-model sur les balises "input-file", vous devez donc le faire de manière "native" transmettre tous les fichiers (éventuellement) sélectionnés de l'utilisateur.

contrôleur

$scope.uploadFile = function(files) {
    var fd = new FormData();
    //Take the first selected file
    fd.append("file", files[0]);

    $http.post(uploadUrl, fd, {
        withCredentials: true,
        headers: {'Content-Type': undefined },
        transformRequest: angular.identity
    }).success( ...all right!... ).error( ..damn!... );

};

La partie cool est le type de contenu non défini et le transformRequest: angular.identity qui donne à $ http la possibilité de choisir le "type de contenu" approprié et de gérer les limites nécessaires au traitement de données en plusieurs parties.

183
Fabio Bonfante

Vous pouvez utiliser la directive simple/légère ng-file-upload . Il prend en charge le glisser-déposer, la progression de fichier et le téléchargement de fichier pour les navigateurs autres que HTML5 avec FileAPI flash shim

<div ng-controller="MyCtrl">
  <input type="file" ngf-select="onFileSelect($files)" multiple>
</div>

JS:

//inject angular file upload directive.
angular.module('myApp', ['ngFileUpload']);

var MyCtrl = [ '$scope', 'Upload', function($scope, Upload) {
  $scope.onFileSelect = function($files) {
  Upload.upload({
    url: 'my/upload/url',
    file: $files,            
  }).progress(function(e) {
  }).then(function(data, status, headers, config) {
    // file is uploaded successfully
    console.log(data);
  }); 

}];
42
danial

Il est plus efficace d’envoyer un fichier directement.

Le codage base64 de Content-Type: multipart/form-data ajoute une surcharge supplémentaire de 33%. Si le serveur le prend en charge, il est plus efficace d’envoyer directement les fichiers:

$scope.upload = function(url, file) {
    var config = { headers: { 'Content-Type': undefined },
                   transformResponse: angular.identity
                 };
    return $http.post(url, file, config);
};

Lors de l'envoi d'un POST avec un objet File , il est important de définir 'Content-Type': undefined. La méthode d'envoi XHR détectera alors le objet Fichier et définira automatiquement le type de contenu.

Pour envoyer plusieurs fichiers, voir Faire plusieurs $http.post demandes directement à partir d'une liste de fichiers


J'ai pensé que je devrais commencer par input type = "file", mais j'ai ensuite découvert que AngularJS ne peut pas se lier à cela ..

L'élément <input type=file> ne fonctionne pas par défaut avec la directive ng-model . Il faut une directive personnalisée :

Démo de travail de la directive "select-ng-files" fonctionnant avec ng-model1

angular.module("app",[]);

angular.module("app").directive("selectNgFiles", function() {
  return {
    require: "ngModel",
    link: function postLink(scope,elem,attrs,ngModel) {
      elem.on("change", function(e) {
        var files = elem[0].files;
        ngModel.$setViewValue(files);
      })
    }
  }
});
<script src="//unpkg.com/angular/angular.js"></script>
  <body ng-app="app">
    <h1>AngularJS Input `type=file` Demo</h1>
    
    <input type="file" select-ng-files ng-model="fileArray" multiple>
    
    <h2>Files</h2>
    <div ng-repeat="file in fileArray">
      {{file.name}}
    </div>
  </body>

$http.post avec le type de contenu multipart/form-data

S'il faut envoyer multipart/form-data:

<form role="form" enctype="multipart/form-data" name="myForm">
    <input type="text"  ng-model="fdata.UserName">
    <input type="text"  ng-model="fdata.FirstName">
    <input type="file"  select-ng-files ng-model="filesArray" multiple>
    <button type="submit" ng-click="upload()">save</button>
</form>
$scope.upload = function() {
    var fd = new FormData();
    fd.append("data", angular.toJson($scope.fdata));
    for (i=0; i<$scope.filesArray.length; i++) {
        fd.append("file"+i, $scope.filesArray[i]);
    };

    var config = { headers: {'Content-Type': undefined},
                   transformRequest: angular.identity
                 }
    return $http.post(url, fd, config);
};

Lors de l'envoi d'un POST avec la FormData API , il est important de définir 'Content-Type': undefined. La méthode d'envoi XHR détectera alors l'objet FormData et définira automatiquement l'en-tête de type de contenu sur multipart/form-data avec le limite appropriée .

7
georgeawg

Je viens d'avoir ce problème. Donc, il y a quelques approches. La première est que les nouveaux navigateurs supportent la

var formData = new FormData();

Suivez ce lien vers un blog avec des infos sur comment l'assistance est limitée aux navigateurs modernes, mais sinon, cela résout totalement ce problème.

Sinon, vous pouvez poster le formulaire sur un iframe en utilisant l'attribut target. Lorsque vous postez le formulaire, veillez à définir la cible sur un iframe avec sa propriété d'affichage définie sur none. La cible est le nom de l'iframe. (Juste pour que vous sachiez.)

J'espère que ça aide

5
Edgar Martinez

Je sais que c'est une entrée tardive, mais j'ai créé une directive de téléchargement simple. Que vous pouvez travailler en un rien de temps!

<input type="file" multiple ng-simple-upload web-api-url="/api/post"
       callback-fn="myCallback" />

ng-simple-upload plus sur Github avec un exemple utilisant Web API.

2
shammelburg

Vous pouvez télécharger via $resource en affectant des données à l'attribut params de la ressource actions comme ceci:

$scope.uploadFile = function(files) {
    var fdata = new FormData();
    fdata.append("file", files[0]);

    $resource('api/post/:id', { id: "@id" }, {
        postWithFile: {
            method: "POST",
            data: fdata,
            transformRequest: angular.identity,
            headers: { 'Content-Type': undefined }
        }
    }).postWithFile(fdata).$promise.then(function(response){
         //successful 
    },function(error){
        //error
    });
};
1
Emeka Mbah

Je viens juste d’écrire une simple directive (à partir d’un programme existant) pour un simple utilisateur de téléchargement dans AngularJs.

(Le plugin exact pour le programme de téléchargement jQuery est https://github.com/blueimp/jQuery-File-Upload )

n téléchargeur simple utilisant AngularJs (avec implémentation CORS)

(Bien que le côté serveur soit pour PHP, vous pouvez aussi le changer facilement)

1
sk8terboi87 ツ

Je voudrais ajouter une alternative. Utilisation du fichier de téléchargement angulaire. Vous pouvez trouver une explication:

Vous pouvez trouver une explication détaillée ici: https://github.com/nervgh/angular-file-upload Un exemple: https://github.com/nervgh/angular-file -upload/tree/master/examples/simple

0
Leo Much