Angularjs $http post file и данные формы
У меня есть следующий запрос в 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)
которые отправляют файл и формируют поля на бэкэнд. Как я могу сделать то же самое (отправка файла + поля формы) с помощью Angular $http. В настоящее время мне это нравится, но не знаю, как отправить файл.
var payload = {"Name":"abc"};
$http.post('/test', payload)
.success(function (res) {
//success
});
Ответы
Ответ 1
Недавно я написал директиву, поддерживающую несколько загрузок файлов. Решение, которое я создал, зависит от службы, чтобы заполнить пробел, который вы идентифицировали с помощью службы $http. Я также включил директиву, которая предоставляет простой API для вашего модуля angular для публикации файлов и данных.
Пример использования:
<lvl-file-upload
auto-upload='false'
choose-file-button-text='Choose files'
upload-file-button-text='Upload files'
upload-url='http://localhost:3000/files'
max-files='10'
max-file-size-mb='5'
get-additional-data='getData(files)'
on-done='done(files, data)'
on-progress='progress(percentDone)'
on-error='error(files, type, msg)'/>
Вы можете найти код на github, а документацию на my блог
Это зависит от вас, чтобы обрабатывать файлы в вашей веб-среде, но созданное мной решение обеспечивает интерфейс angular для получения данных на ваш сервер. Код angular, который вам нужно написать, - это отвечать на события загрузки
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*/ };
});
Ответ 2
У меня была аналогичная проблема, когда приходилось загружать файл и отправлять данные токена пользователя одновременно. transformRequest
вместе с формированием FormData
помогли:
$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) {
});
Для получения файла $scope.file
я использовал настраиваемую директиву:
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:
<input type="file" file="file" required />
Ответ 3
Мне не удалось заставить Павла отвечать, как при публикации в приложении Web.Api.
Проблема связана с удалением заголовков.
headersGetter();
delete headers['Content-Type'];
Чтобы браузеру разрешили по умолчанию использовать Content-Type вместе с граничным параметром, мне нужно было установить Content-Type на undefined. Используя пример Павла, граница никогда не была установлена, что привело к исключению 400 HTTP.
Ключ должен был удалить код, удаляющий заголовки, показанные выше, и установить тип содержимого заголовков пустым вручную. Таким образом, браузер может установить свойства.
headers: {'Content-Type': undefined}
Вот полный пример.
$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;
}
Ответ 4
Вы также можете загрузить с помощью HTML5. Вы можете использовать этот загрузчик AJAX.
Код JS в основном:
$scope.doPhotoUpload = function () {
// ..
var myUploader = new uploader(document.getElementById('file_upload_element_id'), options);
myUploader.send();
// ..
}
Что читается из входного элемента HTML
<input id="file_upload_element_id" type="file" onchange="angular.element(this).scope().doPhotoUpload()">
Ответ 5
вот мое решение:
// 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);
});
<div>
<input type="file" accept="image/*" onchange="angular.element( this ).scope().uploadImg( this.files )">
<textarea ng-model="data.desc" />
<button type="button" ng-click="update()">Update</button>
</div>
Ответ 6
Существуют и другие решения, которые вы можете посмотреть в http://ngmodules.org/modules/ngUpload, как обсуждалось здесь интеграция с загрузчиком файлов для angularjs
Ответ 7
В моем решении у меня есть
$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);
};
}
Ответ 8
Пожалуйста, посмотрите мою реализацию. Вы можете обернуть следующую функцию в службу:
function(file, url) {
var fd = new FormData();
fd.append('file', file);
return $http.post(url, fd, {
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
});
}
Обратите внимание, что аргумент file
имеет значение Blob
. Если у вас есть версия файла base64
- ее можно легко изменить на Blob
следующим образом:
fetch(base64).then(function(response) {
return response.blob();
}).then(console.info).catch(console.error);