如何从上传图像的 AngularJS $http POST 请求中获取“进度”事件?是否可以在客户端执行此操作,或者我是否需要服务器在接收数据时报告进度?
6 回答
使用纯角度:
function upload(data) {
var formData = new FormData();
Object.keys(data).forEach(function(key){formData.append(key, data[key]);});
var defer = $q.defer();
$http({
method: 'POST',
data: formData,
url: <url>,
headers: {'Content-Type': undefined},
uploadEventHandlers: { progress: function(e) {
defer.notify(e.loaded * 100 / e.total);
}}
}).then(defer.resolve.bind(defer), defer.reject.bind(defer));
return defer.promise;
}
和其他地方......
// file is a JS File object
upload({avatar:file}).then(function(responce){
console.log('success :) ', response);
}, function(){
console.log('failed :(');
}, function(progress){
console.log('uploading: ' + Math.floor(progress) + '%');
});
You can also use the simple/lightweight angular-file-upload directive that takes care of these stuff. It supports drag&drop, file progress/abort and file upload for non-HTML5 browsers with FileAPI flash shim
<div ng-controller="MyCtrl">
<input type="file" ng-file-select="onFileSelect($files)" multiple>
</div>
JS:
//inject angular file upload directive.
angular.module('myApp', ['angularFileUpload']);
var MyCtrl = [ '$scope', '$upload', function($scope, $upload) {
$scope.onFileSelect = function($files) {
//$files: an array of files selected, each file has name, size, and type.
for (var i = 0; i < $files.length; i++) {
var $file = $files[i];
$upload.upload({
url: 'my/upload/url',
file: $file,
progress: function(e){}
}).then(function(data, status, headers, config) {
// file is uploaded successfully
console.log(data);
});
}
}
}];
我不认为 $http.post() 可以用于此。
至于客户端,它应该与 HTML5 浏览器一起使用,但您可能必须创建自己的 XMLHttpRequest 对象和onprogress
侦听器。请参阅AngularJS:跟踪同时上传的每个文件的状态以获取想法。
我认为 Angular 没有内置的东西来处理上传。
我认为你最好的选择是使用jQuery File Upload 之类的东西。解决方案的一个想法是创建一个{progress:0}
默认返回的服务,然后在其内部实现 jQuery File Upload 的进度更新回调,然后简单地不断更新进度。由于 Angular 的绑定,上传进度会同步。
angular.module('myApp.services', [])
.factory('Uploader', function() {
var uploaderService = {};
var status = { progress: 0 };
uploaderService.upload = function(inputEl) {
inputEl.fileupload({
/* ... */
progressall: function (e, data) {
status.progress = parseInt(data.loaded / data.total * 100, 10);
}
});
};
return uploaderService;
});
这是另一个解决方案:
window.XMLHttpRequest = (function (orig) {
if (orig) {
var intercept = [],
result = function () {
var r = new orig();
if (r.upload) {
$(r).on(
'abort error load loadend loadstart progress',
function (e) {
$(document).trigger('upload.XHR', e);
}
);
}
if (intercept.length) {
intercept[0].push({
request:r
});
}
return r;
};
result.grab = function (f) {
intercept.unshift([]);
f();
return intercept.shift();
};
return result;
}
return function () {
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {}
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {}
try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {}
throw new Error("This browser does not support XMLHttpRequest.");
};
}(window.XMLHttpRequest));
笔记:
AngularJS 当前将引用存储
window.XMLHttpRequest
为私有XHR
变量,然后像这样使用它new XHR()
:我怀疑这会不会改变,所以上面类似 shim 的代码应该可以正常工作。Mozilla 有一些扩展:
XMLHttpRequest
接受可选参数。上面的代码没有处理这个问题,但是 AngularJS 并没有使用这些扩展。一种可能的用途(如果您想显示所有当前请求,并可能实现一些“取消”按钮):
$(document).on('upload.XHR', function (_e, e) {
switch (e.type) {
// do your thing here
}
});
- 另一种可能的用途:
var list = window.XMLHttpRequest.grab(function () {
// start one or more $http requests here, or put some code
// here that indirectly (but synchronously) starts requests
$http.get(...);
couchDoc.save();
couchDoc.attach(blob, 'filename.ext');
// etc
});
list[0].request.upload.addEventListener(...);
- 或者,您可以将这两种方法与对上面代码的一些修改结合起来。
你可以在我使用简单的角度函数上传文件和 $scope.progressBar 变量来检查上传进度的地方使用它...
$scope.functionName = function(files) {
var file = files[0];
$scope.upload = $upload.upload({
url: 'url',
method: 'POST',
withCredentials: true,
data: {type:'uploadzip'},
file: file, // or list of files ($files) for html5 only
}).progress(function(evt) {
console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
$scope.progressBar = parseInt(100.0 * evt.loaded / evt.total);
}).success(function(data, status, headers, config) {
console.log('upload succesfully...')
}).error(function(err) {
console.log(err.stack);
})
}