【发布时间】:2013-01-12 02:39:35
【问题描述】:
如何从上传图像的 AngularJS $http POST 请求中获取“进度”事件?是否可以在客户端执行此操作,还是需要服务器在接收数据时报告进度?
【问题讨论】:
-
您需要准确的信息,例如“完成 10%”,然后是“完成 25%”等,还是只需要某种动画图形来表明工作正在进行?
标签: angularjs
如何从上传图像的 AngularJS $http POST 请求中获取“进度”事件?是否可以在客户端执行此操作,还是需要服务器在接收数据时报告进度?
【问题讨论】:
标签: angularjs
使用纯角度:
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) + '%');
});
【讨论】:
我认为 $http.post() 不能用于此。
至于客户端,它应该与 HTML5 浏览器一起使用,但您可能必须创建自己的 XMLHttpRequest 对象和onprogress 侦听器。有关想法,请参阅 AngularJS: tracking status of each file being uploaded simultaneously。
【讨论】:
您还可以使用简单/轻量级的angular-file-upload 指令来处理这些内容。 它通过 FileAPI flash shim 支持非 HTML5 浏览器的拖放、文件进度/中止和文件上传
<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);
});
}
}
}];
【讨论】:
onprogress 上使用了scope.$apply(...) 来显示百分比范围。
你可以在我使用简单的角度函数上传文件和 $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);
})
}
【讨论】:
我认为 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(...);
【讨论】: