web-dev-qa-db-fra.com

Demande en plusieurs parties avec AngularJS

J'ai un point de terminaison API auquel je dois envoyer une requête HTTP en plusieurs parties, composée de deux parties, file (un fichier de système de fichiers) et data (un objet JSON).

Après quelques recherches, j'ai découvert comment faire une demande en plusieurs parties dans AngularJS:

$http({
   method: 'POST',
   url: url,
   headers: {
      'Content-Type': 'multipart/form-data'
   },
   data: {
      data: model,
      file: file
   },
   transformRequest: customFormDataObject
});

1) La fonction customFormDataObject avait initialement cette forme:

customFormDataObject formDataObject = function (data, headersGetter) {
   var fd = new FormData();
   angular.forEach(data, function (value, key) {
      fd.append(key, value);
   });

   var headers = headersGetter();
   delete headers['Content-Type'];

   return fd;
};

Le résultat de cette implémentation est que les parties individuelles de la demande n'ont pas de contentType défini.

2) Après avoir lu un peu plus ( https://stackoverflow.com/a/24535293/689216 ) J'ai essayé d'utiliser un Blob pour cela, l'objet customFormData ressemblant à ceci (un peu en désordre, fondamentalement la première partie sera de contentTypeapplication/json, le deuxième image/png):

customFormDataObject = function (data, headersGetter) {
    var fd = new FormData();

    var contentType = 'application/json';
    angular.forEach(data, function (value, key) {
         fd.append(key, new Blob([JSON.stringify(value)], {
             type: contentType
         }));
         contentType = 'image/png';
    });

    var headers = headersGetter();
    delete headers['Content-Type'];

    return fd;
 };

Cette deuxième approche définit le contentType correct pour chaque partie de la demande, mais elle ne définit aucune valeur pour les parties.

Fondamentalement, ce qui se passe est avec 1) les valeurs correctes sont définies dans les parties multiples, mais les contentType ne sont pas définies. Avec 2) les contentType sont définis, mais pas les valeurs pour les parties multiples.

Suis-je en train de manquer quelque chose? Cette fonctionnalité n'est-elle pas censée fonctionner comme ça?

Merci!

19
Adrian Marinica

La façon la plus simple de télécharger des fichiers dans Angular:

var fd = new FormData();
fd.append('file', file);
fd.append('data', 'string');
$http.post(uploadUrl, fd, {
   transformRequest: angular.identity,
   headers: {'Content-Type': undefined}
})
.success(function(){
})
.error(function(){
});

Les deux propriétés suivantes de l'objet config sont absolument essentielles:

transformRequest: angular.identity

remplace la sérialisation par défaut d'Angular, laissant nos données intactes.

headers: {'Content-Type': undefined }

permet au navigateur de détecter la bonne Content-Type comme multipart/form-data, et remplissez la limite correcte.

Rien d'autre n'a fonctionné pour moi! Avec l'aimable autorisation de Lady Louthan merveilleux blogpost .

30
Sensei James

Je résous exactement le même problème.

Après quelques tests, j'ai eu cette situation que vous avez décrite:

Fondamentalement, ce qui se passe avec 1) les valeurs correctes sont définies dans les parties multiples, mais les contentType ne sont pas définis. Avec 2) les contentType sont définis, mais pas les valeurs pour les parties multiples.

Pour résoudre ce problème, j'ai dû utiliser Blob et Post Ajax au lieu de $ http Post.

Il semble que $ http ne fonctionne pas correctement dans ce cas.

Code:

    var formData = new FormData();

    var blobId = new Blob([100], { 'type':'text/plain' });
    formData.append('testId', blobId);

    var blobImage = fileService.base64ToBlob(contentToDecode, 'image/jpeg');
    formData.append('file', blobImage, 'imagem' + (i + 1) + '.jpg');

Demande:

    $.ajax({
      url: url,
      data: formData,
      cache: false,
      contentType: false,
      processData: false,
      type: 'POST',
      success: function(response) {
        deferred.resolve(response);
        $rootScope.requestInProgress = false;
      },
      error: function(error) {
        deferred.reject(error);
        $rootScope.requestInProgress = false;
      }
    });
4
Lucas Leal

Avez-vous essayé quelque chose comme ça:

$httpProvider.defaults.transformRequest = function(data) {
  if (data === undefined){
    return data;
  }

  var formData = new FormData();
  for (var key in data){
    if(typeof data[key] == 'object' && !(data[key] instanceof File)){
      formData.append(key, JSON.stringify(data[key]));
    }else{
      formData.append(key, data[key]);
    }
  }
  return formData;
};
4
Duncan

Vous pouvez utiliser https://github.com/danialfarid/ng-file-upload/ .

Dans ce programme de téléchargement de fichiers, il est prévu d'envoyer le fichier et les données (au format JSON) séparément, comme vous l'avez mentionné dans votre question ci-dessus.

Pour Ex: -

var upload = $upload.upload({
                 url: url,
                 file: file,
                 method: 'POST' or 'PUT', default POST,
                 headers: {'Content-Type': 'multipart/form-data'}, // only for html5
                 fileName: 'doc.jpg',
                 fileFormDataName: 'myFile',
                 data: {'data': model}
             });

Dans le code ci-dessus, vous pouvez envoyer une demande POST ou PUT avec 'multipart/form-data', fichier et objet de données au format JSON séparément.

Pour plus d'informations, vous pouvez visiter le lien ci-dessus et consulter le fichier ReadMe.md du plug-in.

Je sais que mon approche est un peu différente de celle que vous suivez actuellement, mais l'objectif est le même.

2
Ravi Teja

ce que j'ai fait pour résoudre ce problème était.

var formData = new FormData(document.getElementById('myFormId'));

puis à mon service

                var deferred = $q.defer();
                $http.post('myurl', formData, {
                    cache: false,
                    contentType: false,
                    processData: false,
                })
                    .success(function (response) {
                        deferred.resolve(response);
                    })
                    .error(function (reject) {
                        deferred.reject(reject);
                    });
                return deferred.promise;
0
themightysapien