Angularjs $ http post file e dados de formulário

Eu tenho o pedido abaixo em python

import requests, json, io cookie = {} payload = {"Name":"abc"} url = "/test" file = "out/test.json" fi = {'file': ('file', open(file) )} r = requests.post("http://192.168.1.1:8080" + url, data=payload, files=fi, cookies=cookie) print(r.text) 

que enviam um arquivo e formam campos para o back-end. Como posso fazer o mesmo (enviando arquivos + campos de formulário) com $ http Angular. Atualmente, eu gosto disso, mas não sei como enviar o arquivo também.

 var payload = {"Name":"abc"}; $http.post('/test', payload) .success(function (res) { //success }); 

Recentemente, escrevi uma diretiva que suporta vários uploads nativos de arquivos. A solução que criei depende de um serviço para preencher a lacuna que você identificou com o serviço $ http. Eu também incluí uma diretiva, que fornece uma API fácil para o seu módulo angular usar para postar os arquivos e dados.

Exemplo de uso:

  

Você pode encontrar o código no github e a documentação no meu blog

Cabe a você processar os arquivos em sua estrutura da Web, mas a solução que criei fornece a interface angular para obter os dados em seu servidor. O código angular que você precisa escrever é responder aos events de upload

 angular .module('app', ['lvl.directives.fileupload']) .controller('ctl', ['$scope', function($scope) { $scope.done = function(files,data} { /*do something when the upload completes*/ }; $scope.progress = function(percentDone) { /*do something when progress is reported*/ }; $scope.error = function(file, type, msg) { /*do something if an error occurs*/ }; $scope.getAdditionalData = function() { /* return additional data to be posted to the server*/ }; }); 

Eu tive problema semelhante quando tive que fazer o upload do arquivo e enviar informações do token do usuário ao mesmo tempo. transformRequest junto com a formação de FormData ajudou:

  $http({ method: 'POST', url: '/upload-file', headers: { 'Content-Type': 'multipart/form-data' }, data: { email: Utils.getUserInfo().email, token: Utils.getUserInfo().token, upload: $scope.file }, transformRequest: function (data, headersGetter) { var formData = new FormData(); angular.forEach(data, function (value, key) { formData.append(key, value); }); var headers = headersGetter(); delete headers['Content-Type']; return formData; } }) .success(function (data) { }) .error(function (data, status) { }); 

Para obter o arquivo $scope.file , usei a diretiva personalizada:

 app.directive('file', function () { return { scope: { file: '=' }, link: function (scope, el, attrs) { el.bind('change', function (event) { var file = event.target.files[0]; scope.file = file ? file : undefined; scope.$apply(); }); } }; }); 

Html:

  

Não consegui fazer com que a resposta de Pavel funcionasse como quando postando em um aplicativo Web.Api.

O problema parece estar com a exclusão dos headers.

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

Para garantir que os navegadores pudessem padronizar o Content-Type junto com o parâmetro boundary, eu precisava definir o Content-Type como indefinido. Usando o exemplo de Pavel, o limite nunca foi definido, resultando em uma exceção HTTP 400.

A chave era remover o código excluindo os headers mostrados acima e definir o tipo de conteúdo de headers como nulo manualmente. Permitindo assim que o navegador defina as propriedades.

 headers: {'Content-Type': undefined} 

Aqui está um exemplo completo.

 $scope.Submit = form => { $http({ method: 'POST', url: 'api/FileTest', headers: {'Content-Type': undefined}, data: { FullName: $scope.FullName, Email: $scope.Email, File1: $scope.file }, transformRequest: function (data, headersGetter) { var formData = new FormData(); angular.forEach(data, function (value, key) { formData.append(key, value); }); return formData; } }) .success(function (data) { }) .error(function (data, status) { }); return false; } 

Você também pode fazer o upload usando HTML5. Você pode usar este uploader AJAX .

O código JS é basicamente:

  $scope.doPhotoUpload = function () { // .. var myUploader = new uploader(document.getElementById('file_upload_element_id'), options); myUploader.send(); // .. } 

Qual lê de um elemento de input HTML

  

aqui está a minha solução:

 // Controller $scope.uploadImg = function( files ) { $scope.data.avatar = files[0]; } $scope.update = function() { var formData = new FormData(); formData.append('desc', data.desc); formData.append('avatar', data.avatar); SomeService.upload( formData ); } // Service upload: function( formData ) { var deferred = $q.defer(); var url = "/upload" ; var request = { "url": url, "method": "POST", "data": formData, "headers": { 'Content-Type' : undefined // important } }; console.log(request); $http(request).success(function(data){ deferred.resolve(data); }).error(function(error){ deferred.reject(error); }); return deferred.promise; } // backend use express and multer // a part of the code var multer = require('multer'); var storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, '../public/img') }, filename: function (req, file, cb) { cb(null, file.fieldname + '-' + Date.now() + '.jpg'); } }) var upload = multer({ storage: storage }) app.post('/upload', upload.single('avatar'), function(req, res, next) { // do something console.log(req.body); res.send(req.body); }); 
 

Existem outras soluções que você pode consultar em http://ngmodules.org/modules/ngUpload, conforme discutido aqui . Integração do uploader de arquivos para o angularjs

Na minha solução, eu tenho

 $scope.uploadVideo = function(){ var uploadUrl = "/api/uploadEvent"; //obj with data, that can be one input or form file = $scope.video; var fd = new FormData(); //check file form on being for (var obj in file) { if (file[obj] || file[obj] == 0) { fd.append(obj, file[obj]); } } //open XHR request var xhr = new XMLHttpRequest(); // $apply to rendering progress bar for any chunking update xhr.upload.onprogress = function(event) { $scope.uploadStatus = { loaded: event.loaded, total: event.total }; $scope.$apply(); }; xhr.onload = xhr.onerror = function(e) { if (this.status == 200 || this.status == 201) { //sucess $scope.uploadStatus = { loaded: 0, total: 0 }; //this is for my solution $scope.video = {}; $scope.vm.model.push(JSON.parse(e.currentTarget.response)); $scope.$apply(); } else { //on else status } }; xhr.open("POST", uploadUrl, true); //token for upload, thit for my solution xhr.setRequestHeader("Authorization", "JWT " + window.localStorage.token); //send xhr.send(fd); }; 

}

Por favor, dê uma olhada na minha implementação. Você pode envolver a seguinte function em um serviço:

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

Por favor note que file argumento do file é um Blob . Se você tem a versão base64 de um arquivo, pode ser facilmente alterado para o Blob forma:

 fetch(base64).then(function(response) { return response.blob(); }).then(console.info).catch(console.error);