How can I get a 'progress' event from my AngularJS $http POST request that is uploading an image? Is it possible to do this client-side, or do I need the server to report the progress as it receives the data?
Using pure angular:
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;
}
and somewhere else ...
// 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);
});
}
}
}];
I don't think $http.post() can be used for this.
As for client-side, it should work with an HTML5 browser, but you'll probably have to create your own XMLHttpRequest object and onprogress listener. See AngularJS: tracking status of each file being uploaded simultaneously for ideas.
I don't think Angular has something built-in to handle uploads.
I think your best bet is to use something like jQuery File Upload. An idea for a solution would to create a Service that returns {progress:0} as default and then inside itself, implements the jQuery File Upload's progress update callback, which then simply keeps updating the progress. Thanks to Angular's binding, the upload progress would be in sync.
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;
});
Here is another solution:
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));
Notes:
AngularJS currently stores a reference to window.XMLHttpRequest as private XHR variable, then uses it like this: new XHR(). I doubt this will ever change, so the shim-like code above should work just fine.
Mozilla has some extensions: XMLHttpRequest accepts optional arguments. The code above does not handle this, but AngularJS does not use these extensions anyway.
One of possible uses (if you want to show all current requests, and maybe implement some "Cancel" button):
$(document).on('upload.XHR', function (_e, e) {
switch (e.type) {
// do your thing here
}
});
Another possible use:
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(...);
Or, you can combine both approaches with some modifications to the code above.
you can use this where Im using simple angular function to upload file and $scope.progressBar variable to check the progress of uploading...
$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);
})
}
Related
console log
angular.module('Kinder.pages.member')
.controller('MemberInfoCtrl', MemberInfoCtrl);
function MemberInfoCtrl($scope,MemberModel,Constants,fileReader,$filter,AppUtils,$http,toastr,API) {
var vm = this;
vm.member = {};
vm.member.memberType = "1";
vm.member.memberSex = "1";
vm.member.avatarPath = "";
$scope.getFile = function () {
fileReader.readAsDataUrl($scope.file, $scope);
var fileInput = document.getElementById('uploadFile').files[0];
if(!AppUtils.isUndefinedOrNull(fileInput)){
var formData=new FormData();
formData.append("picUrl",fileInput);
$http({
........
}).success(function(data, status) {
console.log(data);
if(data.stat == 'success'){
//vm will have avatarPath value in there
console.log(vm);
vm.member.avatarPath = data.path;
//vm will have avatarPath value in there
console.log(vm);
}
}).error(function(data, status) {
});
}
};
$scope.submit = function() {
//vm had loss avatarPath ...
console.log(vm.member);
};
}
I do the function is to upload pictures before the preview, upload to the server to return to the path, assigned to vm。
But I can not get the avatarPath value outside the $ scope.getFile method。
I suspect that the problem of the scope of the problem, but I can not find a solution, I am the angular novice。
So who can tell me what this is for the reason。
I used google translation to describe the above questions,
I do not know if you understand me。。。
Anyway, thank you for taking the time to browse this question!
i fix it!
thanks for #JayantPatil reminders!
i am declaring controller in the route
.state('member.info', {
url: '/info/{memberId:string}',
params: {
memberId: null
},
templateUrl: 'app/pages/member/info/member-info.html',
controller: 'MemberInfoCtrl as vm'
});
and in the html , i use the ng-controller
<div class="userpic" ng-controller="MemberInfoCtrl as vm">
<input type="file" ng-show="false" id="uploadFile" ng-file-select/>
</div>
cause i duplicate declarations the controller , maybe the Scopes is Different,
that is all
I am try to collect data and images from local server (Acquia Dev Desktop) using this Angular Js code
controller
var app = angular.module('App', []);
app.controller('Ctrl', function($scope, $http) {
$scope.images = [];
$http({
method : "GET",
url : 'http://docroot.com.dd:8083/catalogue/11/images/json'
}).then(function mySucces(response) {
$scope.images = response.data;
}, function myError(response) {
$scope.images = response.statusText;
});
});
Json
[{"image":" <a href=\"http:\/\/docroot.com.dd:8083\/sites\/docroot.com.dd\/files\/catalogues\/2016-09\/images\/Pty%20Prs.compressedjpg_Page1.jpg\">Property Press.compressedjpg_Page1.jpg<\/a>"}]
// i got out put like this :
<a href=\"http:\/\/docroot.com.dd:8083\/sites\/docroot.com.dd\/files\/catalogues\/2016-09\/images\/Pty%20Prs.compressedjpg_Page1.jpg\">Property Press.compressedjpg_Page1.jpg<\/a>
i need to collect only image url instead of the whole link ,
Well, sending HTML elements in a JSON doesn't seem good to but, anyway if you cannot change it...
For my part, I would parse the html string with the built-in XML parser.
Here is the code taken from this answer
//XML parser
var parseXml;
if (typeof window.DOMParser != "undefined") {
parseXml = function(xmlStr) {
//should work with any recent browser
return ( new window.DOMParser()).parseFromString(xmlStr, "text/xml");
};
} else if (typeof window.ActiveXObject != "undefined" &&
new window.ActiveXObject("Microsoft.XMLDOM")) {
//This part is intended to very old browsers
parseXml = function(xmlStr) {
var xmlDoc = new window.ActiveXObject("Microsoft.XMLDOM");
xmlDoc.async = "false";
xmlDoc.loadXML(xmlStr);
return xmlDoc;
};
} else {
throw new Error("No XML parser found");
}
//Your code
var jsonContent= [{"image":" <a href=\"http:\/\/docroot.com.dd:8083\/sites\/docroot.com.dd\/files\/catalogues\/2016-09\/images\/Pty%20Prs.compressedjpg_Page1.jpg\">Property Press.compressedjpg_Page1.jpg<\/a>"}];
var elem = jsonContent[0].image;
var link = parseXml(elem);
try {
document.getElementById("out").innerHTML = link.documentElement.getAttribute("href");
} catch (e) {
alert(e);
}
<span id="out" />
I have been able to successfully upload an image with a custom field template calling a function that handles the upload. It then gathers the return url and from there, i have no idea how to insert it back into the field for saving with the form.
Any help would be much appreciated :)
Here is my code:
var myApp = angular.module('myApp', ['ng-admin', 'backand', 'ngFileUpload']);
//myApp.directive('dashboardSummary', require('./dashboards/dashboardSummary'));
myApp.config(['BackandProvider', function(BackandProvider) {
BackandProvider.setAppName('');
BackandProvider.setSignUpToken('');
BackandProvider.setAnonymousToken('');
}]);
myApp.config(function(RestangularProvider) {
// add a response interceptor
RestangularProvider.addResponseInterceptor(function(data, operation, what, url, response, deferred) {
var extractedData;
// .. to look for getList operations
if (operation === "getList") {
// .. and handle the data and meta data
extractedData = data.data;
extractedData.meta = data.data.__metadata;
} else {
extractedData = data;
}
return extractedData;
});
});
myApp.config(['NgAdminConfigurationProvider','BackandProvider', function (nga, BackandProvider, $scope) {
// create an admin application
BackandProvider.setAppName('');
BackandProvider.setSignUpToken('');
BackandProvider.setAnonymousToken('');
var admin = nga.application('Pocket Release Admin Manager')
.baseApiUrl('https://api.backand.com/1/objects/'); // main API endpoint#
// Users
var user = nga.entity('users');
user.listView().fields([
nga.field('firstName').isDetailLink(true),
nga.field('lastName'),
nga.field('email')
]);
user.creationView().fields([
nga.field('firstName'),
nga.field('lastName'),
nga.field('email', 'email')
]);
user.editionView().fields(user.creationView().fields());
// add the user entity to the admin application
admin.addEntity(user);
// Platforms
var platforms = nga.entity('platforms');
platforms.listView().fields([
nga.field('id'),
nga.field('platform_name'),
]);
platforms.creationView().fields([
nga.field('id'),
nga.field('platform_name'),
nga.field('platform_id')
]);
platforms.editionView().fields(platforms.creationView().fields());
admin.addEntity(platforms);
var data = {};
// Games
var games = nga.entity('games');
games.listView().fields([
nga.field('id'),
nga.field('game_title').isDetailLink(true),
nga.field('game_url'),
nga.field('platforms', 'reference')
.targetEntity(platforms)
.targetField(nga.field('platform_name'))
.label('Platform')
]);
games.creationView().fields([
nga.field('game_title'),
nga.field('image').template('<img ng-src="{{game_url}}" ng-show="game_url">'),
nga.field('game_url', 'file').uploadInformation({'url':'', 'apifilename':'game_url'}).template('<div ng-controller="main"><label class="btn btn-default btn-file">Browse<input id="fileInput" field="::field" value="entry.values[{{main}}]" entry="entry" entity="::entity" form="formController.form" datastore="::formController.dataStore" type="file" style="display: none;" accept="*/*" ng-click="initUpload()" /></label></div>', true),
nga.field('platforms'),
nga.field('game_description'),
nga.field('game_boxart'),
nga.field('game_release_uk', 'date'),
nga.field('game_release_us', 'date'),
nga.field('game_release_eu', 'date')
]);
games.editionView().fields(games.creationView().fields());
admin.addEntity(games);
// Dash
admin.dashboard(nga.dashboard()
.addCollection(nga.collection(games)
.name('total_games')
.title('Total Games')
.fields([
nga.field('game_title')
])
.sortField('game_title')
.sortDir('DESC')
.order(1)
).template(`
<div class="row dashboard-starter"></div>
<dashboard-summary></dashboard-summary>
<div class="row dashboard-content">
<div class="col-lg-6">
<div class="panel panel-default">
<ma-dashboard-panel collection="dashboardController.collections.total_games" entries="dashboardController.entries.total_games" datastore="dashboardController.datastore"></ma-dashboard-panel>
</div>
</div>
</div>
`));
// Menu Customize
// customize menu
admin.menu(nga.menu()
.addChild(nga.menu(user).icon('<span class="glyphicon glyphicon-file"></span> ')) // customize the entity menu icon
.addChild(nga.menu(games).icon('<span class="glyphicon glyphicon-folder-open"></span> ')) // you can even use utf-8 symbols!
.addChild(nga.menu(platforms).icon('<span class="glyphicon glyphicon-tags"></span> '))
);
// attach the admin application to the DOM and execute it
nga.configure(admin);
}]);
myApp.controller('main', function ($scope, $rootScope, $location, $http, Backand) {
// Image upload stuff
$scope.initUpload = function(){
var fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', function(e) {
imageChanged(fileInput);
});
}
function imageChanged(fileInput) {
//read file content
var file = fileInput.files[0];
var reader = new FileReader();
reader.onload = function(e) {
upload(file.name, e.currentTarget.result).then(function(res) {
$scope.imageUrl = res.data.url;
$scope.filename = file.name;
return res.data.url;
}, function(err){
alert(err.data);
});
};
reader.readAsDataURL(file);
};
function upload(filename, filedata) {
// By calling the files action with POST method in will perform
// an upload of the file into Backand Storage
return $http({
method: 'POST',
url : Backand.getApiUrl()+'/1/objects/action/games',
params:{
"name": 'files'
},
headers: {
'Content-Type': 'application/json'
},
// you need to provide the file name and the file data
data: {
"filename": Math.floor(Date.now() / 1000) + ''+ filename.match(/\.[0-9a-z]+$/i),
"filedata": filedata.substr(filedata.indexOf(',') + 1, filedata.length) //need to remove the file prefix type
}
}).success(function(data, status, headers, config) {
//$scope.game_url = data.url;
//$("#game_url").addClass("ng-dirty ng-valid-parse ng-touched").removeClass("ng-pristine ng-untouched");
//$("#game_boxart").val(data.url).addClass("ng-dirty ng-valid-parse ng-touched").removeClass("ng-pristine ng-untouched");
return data.url;
});
};
});
Have you tried making a PUT call to backand in order to save this url in the relevant column?
return $http({
method: 'PUT',
url : Backand.getApiUrl()+'/1/objects/games/YOUR_GAME_ID',
headers: {
'Content-Type': 'application/json'
},
data: {
urlColumn:imageUrl
}
}).success(function(data, status, headers, config) {
.....
});
i'm using Restangular and trying to PUT some data but it seems to lose the BaseUrl.
In the config function i define the BaseUrl for Restangular and others Restangular fields.
Constants.restangularBaseUrl is http://192.168.1.100/api/
RestangularProvider.setBaseUrl(Constants.restangularBaseUrl)
.setRestangularFields({
selfLink: '_links.self.href',
id: '_id',
etag: '_etag'
})
.addResponseInterceptor(function(data, operation, what, url, response, deferred){
if (operation === 'getList') {
var result = data._items;
result._meta = data._meta;
result._links = data._links;
return result;
}
return data;
});
Then i have some models like this:
(function(){
angular.module('models.ebayItems', ['services.constants', 'restangular'])
.service('EbayItems', ['Constants', 'Restangular', function (Constants, Restangular) {
Restangular.extendModel('ebayitems', function(model) {
model.toggleMonitor = function(){
var item = this;
Restangular.one('ebayitems', this._id).patch({active: this.active}, '', {'If-Match': this._etag})
.then(function(data){
item._etag = data._etag;
}, function(error){
console.log('error', error);
});
};
return model;
});
var ebayItems = Restangular.all('ebayitems');
var ebayItemsOneSearch = function(_id){
return ebayItems.customGETLIST('', {where: {searchId: _id}});
};
return {
items: ebayItems,
oneSearch: ebayItemsOneSearch
};
}])
})();
Now when i try to do a put request with an item based on that model:
item.put()
it uses the wrong url, i mean it loses the BaseUrl, so instead of putting at:
http://192.168.1.100/api/ebayitems/12345
it puts at
http://192.168.1.100/ebayitems/12345
resulting in a 404 error.
Why?
What am i doing wrong?
Any help really appreciated.
Thank you
The problem was that setting a selfLink field that was a relative url from the API backend it overrides the BaseUrl.
Removing that field from the config function it worked.
RestangularProvider.setBaseUrl(Constants.restangularBaseUrl)
.setRestangularFields({
id: '_id',
etag: '_etag'
})
.addResponseInterceptor(function(data, operation, what, url, response, deferred){
if (operation === 'getList') {
var result = data._items;
result._meta = data._meta;
result._links = data._links;
return result;
}
return data;
});
I already did some research about this issue, but I don't find any problems in the code. If I save the file to disk it looks fine.
The document could not be loaded...
The javascript blob object has bytes.
Response code is 200.
Maybe somebody finds a coding issue?
The html:
<div data-ng-show="vouchercontent">
<embed ng-src="{{vouchercontent}}" style="width:400px;height:700px;"></embed>
</div>
Angular Controller:
$scope.vouchercontent = undefined;
$scope.generateVoucher = function() {
var self = this;
generateVoucherService.generateVoucher($scope.voucherdata).then(function(result) {
var file = new Blob([result], {type: 'application/pdf' });
var fileURL = window.URL.createObjectURL(file);
$scope.vouchercontent = $sce.trustAsResourceUrl(fileURL);
}, function(error) {
alert(error);
});};
Angular Service:
generateVoucher : function(data){
return $http.post('rest/generatevoucher/generate', data, {responseType: 'arraybuffer'})
.then(function(response){
return response.data;
}, function (response) {
return $q.reject(response.data);
});
}
Response in the service:
Response in the controller: