Upload image to server with ionic/angularjs - angularjs

I want upload image to server with ionic.
I use this factory:
(function () {
function ius($q, $ionicLoading, $cordovaFile ) {
var service = {};
service.uploadImage = uploadImage;
return service;
function uploadImage(imageURI) {
var deferred = $q.defer();
var fileSize;
var percentage;
// Find out how big the original file is
window.resolveLocalFileSystemURL(imageURI, function (fileEntry) {
fileEntry.file(function (fileObj) {
fileSize = fileObj.size;
// Display a loading indicator reporting the start of the upload
$ionicLoading.show({ template: 'Uploading Picture : ' + 0 + '%' });
// Trigger the upload
uploadFile();
});
});
function uploadFile() {
// Add the Cloudinary "upload preset" name to the headers
var uploadOptions = {
params: { 'upload_preset': CLOUDINARY_CONFIGS.UPLOAD_PRESET } //CLOUDINARY_CONFIGS.UPLOAD_PRESET
};
$cordovaFile
// Your Cloudinary URL will go here
.uploadFile(CLOUDINARY_CONFIGS.API_URL, imageURI, uploadOptions) //
.then(function (result) {
// Let the user know the upload is completed
$ionicLoading.show({ template: 'Upload Completed', duration: 1000 });
// Result has a "response" property that is escaped
// FYI: The result will also have URLs for any new images generated with
// eager transformations
var response = JSON.parse(decodeURIComponent(result.response));
deferred.resolve(response);
}, function (err) {
// Uh oh!
$ionicLoading.show({ template: 'Upload Failed', duration: 3000 });
deferred.reject(err);
}, function (progress) {
// The upload plugin gives you information about how much data has been transferred
// on some interval. Use this with the original file size to show a progress indicator.
percentage = Math.floor(progress.loaded / fileSize * 100);
$ionicLoading.show({ template: 'Uploading Picture : ' + percentage + '%' });
});
}
return deferred.promise;
}
}
angular.module('App').factory('ImageUploadService', ius);
})();
And i call this function :
'Use Strict';
angular.module('App').controller('editeventController', function ($scope,ImageUploadService) {
$scope.upload = function () {
ImageUploadService.uploadImage("img/test.jpg").then(
function (result) {
var url = result.secure_url || '';
var urlSmall;
if (result && result.eager[0]) urlSmall = result.eager[0].secure_url || '';
// Do something with the results here.
$cordovaCamera.cleanup();
},
function (err) {
// Do something with the error here
$cordovaCamera.cleanup();
});
}
But, when I try to upload, I have this error:
TypeError: window.resolveLocalFileSystemURL is not a function
at Object.uploadImage (http://localhost:8100/js/services/utils.js:177:20)
at Scope.$scope.upload (http://localhost:8100/views/editevent/editevent.js:199:28)
at fn (eval at (http://localhost:8100/lib/ionic/js/ionic.bundle.js:26457:15),
:4:209)
at http://localhost:8100/lib/ionic/js/ionic.bundle.js:62386:9
at Scope.$eval (http://localhost:8100/lib/ionic/js/ionic.bundle.js:29158:28)
at Scope.$apply (http://localhost:8100/lib/ionic/js/ionic.bundle.js:29257:23)
at HTMLButtonElement. (http://localhost:8100/lib/ionic/js/ionic.bundle.js:62385:13)
at HTMLButtonElement.eventHandler (http://localhost:8100/lib/ionic/js/ionic.bundle.js:16583:21)
at triggerMouseEvent (http://localhost:8100/lib/ionic/js/ionic.bundle.js:2948:7)
at tapClick (http://localhost:8100/lib/ionic/js/ionic.bundle.js:2937:3)
[edit]
I use requestFileSystem instead of resolveLocalFileSystemURL
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(window.PERSISTENT, 0, onSuccess, onError);
function onSuccess(fileSystem) {
var directoryEntry = fileSystem.root;
console.log(directoryEntry);
directoryEntry.getFile(imageURI, { create: true, exclusive: false }, function (fileEntry) {
console.log(fileEntry);
fileEntry.createWriter(function (writer) {
// writer.write("This is the text inside readme file");
var pathOfFile = fileEntry.fullPath;
console.log(pathOfFile);
fileSize = fileEntry.size;
// Display a loading indicator reporting the start of the upload
$ionicLoading.show({ template: 'Uploading Picture : ' + 0 + '%' });
uploadFile();
}, function (error) {
alert("Error occurred while writing to file. Error is: " + error.message);
});
}, function (error) {
console.log(error.message);
alert("Error occurred while getting a pointer to file. Error is: " + error.message);
});
}
function onError(evt) {
alert("Error occurred during request to file system pointer. Error is: " + evt.message);
}
Now I have this error:
The operation failed because it would cause the application to exceed
its storage quota.

Related

Angular resolve promise and update existing scope

I am trying to understand Angular's promises and scopes. Actually I implemented the directive bellow. The thing that I want to do is to receive images from server and store locally in order to cache them for next times. Also I want to show a spinner before the load of image is completed and show it after the completion.
How can update variable newUrl into directive when completed all of these promises?
Do you have any idea?
My HTML code is:
<div style="text-align: center;"
cache-src
url="https://upload.wikimedia.org/wikipedia/commons/c/c0/Aix_galericulata_(Male),_Richmond_Park,_UK_-_May_2013.jpg">
</div>
My directive is:
.directive('cacheSrc', [function () {
return {
restrict: 'A',
scope: {
url: '#'
},
controller: 'cacheSrcCtrl',
template: '<img width="100%" ng-if="newUrl!=null" src="{{newUrl}}"><ion-spinner ng-if="newUrl==null" icon="spiral"></ion-spinner>',
};
}])
And the controller of directive has the function bellow:
document.addEventListener('deviceready', function () {
$scope.updateUrl = function (newUrl) {
$scope.newUrl = newUrl;
};
var tmp = $scope.url;
$cordovaSQLite.execute(db, 'SELECT * FROM images where url = (?)', [tmp])
.then(function (result) {
if (result.rows.length > 0) {
$scope.exists = true;
for (var i = 0; i < res.rows.length; i++) {
var image = {
id: res.rows.item(i).id,
url: res.rows.item(i).url,
uri: res.rows.item(i).uri
};
$scope.updateUrl(image.uri);
}
} else {
$scope.exists = false;
var fileTransfer = new FileTransfer();
var uri = encodeURI(tmp);
var uriSave = '';
var fileURL = cordova.file.dataDirectory + uri;//'kaloudiaImages';// + getUUID();// + "DCIM/myFile";
fileTransfer.download(
uri, fileURL, function (entry) {
uriSave = entry.toURL();
KaloudiaDB.add(tmp, fileURL);
$scope.newUrl = fileURL;
$scope.updateUrl(fileURL);
},
function (error) {
console.log("download error code" + error.code);
},
false, {
headers: {
// "Authorization": "Basic dGVzdHVzZXJuYW1lOnRlc3RwYXNzd29yZA=="
}
}
).then(function (data) {
$scope.newUrl = fileURL;
});
}
}, function (error) {
$scope.statusMessage = "Error on saving: " + error.message;
})
.then(function (data) {
$scope.$apply(function () {
$scope.newUrl = fileURL;
});
});
});

Set $http timeout on the fly

I have an Angular project that has a very nice Timeout toaster that pops up if requests are too slow. But the problem is I need much longer timeouts or timeout resets during a File upload (im using ng-file-upload to s3 storage).
My question is: How could I reset a $http timeout during each progress responses or change it to some massive number only during file-uploads:
Here is my interceptor code in my config function:
$httpProvider.interceptors.push(function ($rootScope, $q, toaster) {
return {
request: function (config) {
//config.cache = true;
config.timeout = 6000;
return config;
},
responseError: function (rejection) {
//console.log(rejection);
switch (rejection.status) {
case -1 :
console.log('connection timed out!');
toaster.pop({
type: 'error',
title: 'Server Timed Out!',
body: 'Waiting for request timed out! \n Please check your Internet connection and try again!',
timeout: 6000
});
break;
case 404 :
console.log('Error 404 - not found!');
toaster.pop({
type: 'error',
title: 'Server Timed Out!',
body: 'Error 404! \n Server returned: Not found! Please check your Internet connection and try again!',
timeout: 6000
});
break;
}
return $q.reject(rejection);
}
}
})
Here is my Upload function:
$scope.upload = function (file) {
$scope.count += 1;
file.id= $scope.count;
var durl = apiserv + "api.upload-s3.php?path=" + $scope.folder;
var arr = [];
arr.filename = file.name;
arr.status = "";
arr.progress = 0;
arr.class = "list-group-item-warning";
$scope.files[file.id] = arr;
$http({url: durl}).then(function (drs) {
console.log(drs);
drs.data.file = file;
Upload.upload({
url: drs.data.action, //S3 upload url including bucket name
method: 'POST',
data: {
key: drs.data.key,
acl: drs.data.acl,
Policy: drs.data.Policy,
'X-Amz-Algorithm' : drs.data['X-Amz-Algorithm'],
'X-Amz-Credential' : drs.data['X-Amz-Credential'],
'X-Amz-Date' : drs.data['X-Amz-Date'],
'X-Amz-Signature' : drs.data['X-Amz-Signature'],
//'Content-Type': file.type !== '' ? file.type : 'application/octet-stream',
file: file
}
}).then(function (resp) {
console.log('Success ' + resp.config.data.file.name + 'uploaded. Response: ' + resp.data);
$scope.files[resp.config.data.file.id].status = "Success";
$scope.files[resp.config.data.file.id].progress = 100;
$scope.files[resp.config.data.file.id].class = "list-group-item-success";
}, function (resp) {
console.log('Error status: ' + resp.status);
$scope.files[resp.config.data.file.id].status = "Error: "+ resp.status;
$scope.files[resp.config.data.file.id].progress = 0;
$scope.files[resp.config.data.file.id].class = "list-group-item-danger";
}, function (evt) {
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total);
//console.log('progress: ' + progressPercentage + '% ' + evt.config.data.file.name);
console.log(evt.config.data.file);
$scope.files[evt.config.data.file.id].status = "Uploading...";
$scope.files[evt.config.data.file.id].progress = progressPercentage;
$scope.files[resp.config.data.file.id].class = "list-group-item-warning";
});
});
};
$http's timeout option accepts promises:
timeout – {number|Promise} – timeout in milliseconds, or promise that should abort the request when resolved.
This means that it can be a promise that polls global variable
config.timeout = $q(function (resolve) {
var i = 0;
var interval = setInterval(function () {
i++;
if (i * 1000 >= $rootScope.httpTimeout) {
resolve();
$rootScope.$apply();
clearInterval(interval);
});
}, 1000);
});
or implements any other logic that fits the case.

Wait for forEach to complete before returning value - AngularJS

Overview:
I'm creating an app using ionic framework and AngularJS. The App has 2 forms where user can upload images. In first form, image upload field allows to upload only one image and second form image upload field allows to upload more than one images. After user uploads images I'm showing preview to user right below image field. Then user click on "Save", which calls Web Service (defined into factory) to upload images to site. The Service function implements $q, so that form submission can continue only after uploading images. This is working fine.
Problem:
When uploading multiple images, I'm calling another helper function in controller which loop (forEach) over file data and call file save function for each file. But the execution in this helper function doesn't wait for forEach loop to complete. This is allowing me to get first file details of file saved, but not remaining saved files details.
Here is code I'm using for uploading file:
.controller('AppCtrl', function($q) {
// Upload single file.
$scope.fileUpload = function(fileData) {
var deferred = $q.defer();
if (angular.isUndefined(fileData) || angular.isUndefined(fileData.dataURL)) {
deferred.resolve("No new upload found.");
}
else {
var filedata = {
"file" : {
file: (fileData.dataURL).replace(/^data:image\/[A-Za-z]{3,4};base64,+/g, ''),
filename: fileData.file.name,
},
};
AuthServiceContent.uploadNewFile(filedata).then(function(response) {
deferred.resolve(response);
}, function(response) {
deferred.reject(response);
});
}
return deferred.promise;
};
// Upload multiple files.
$scope.fileUploadMultiple = function(data) {
var deferred = $q.defer();
var fileUploadStatus = [];
if (angular.isUndefined(data) || angular.isUndefined(data[0])) {
deferred.reject("No new upload found.");
}
else {
angular.forEach(data, function(fileData, index) {
$scope.fileUpload(fileData).then(function(response) {
fileUploadStatus[index] = response;
}, function(response) {
});
});
}
return (!angular.isUndefined(fileUploadStatus) ? deferred.resolve(fileUploadStatus) : deferred.reject(fileUploadStatus));
};
$scope.createContent = function(formData) {
$scope.fileUploadMultiple(formData.image).then(function(response) {
if (angular.isObject(response)) {
angular.forEach(response, function(fileData, index) {
console.log(fileData);
formData.image.und = [{'fid' : fileData.fid}];
});
console.log(formData);
}
else {
}
}, function(err) {
console.log("updates failed!!!");
});
return;
};
})
.factory('AuthServiceContent', function($q, $http, DEFAULT) {
var service_content = {
uploadNewFile: function(fileData) {
var deferred = $q.defer();
$http.post(DEFAULT.serviceURL + 'file', JSON.stringify(fileData), {
headers : {
'Content-Type': 'application/json',
'Cookie': 'cookieData',
}
})
.success(function (data, status, headers, config) {
deferred.resolve(data);
})
.error(function (data, status, headers, config) {
deferred.reject(data);
});
return deferred.promise;
}
};
return service_content;
});
I've been trying this for more than 2 days and find similar issue, but its not working.
Updates:
I got this working by adding extra check in loop, here is updated function in controller to upload multiple images.
$scope.fileUploadMultiple = function(data) {
var deferred = $q.defer();
var fileUploadStatus = [];
if (angular.isUndefined(data) || angular.isUndefined(data[0])) {
deferred.reject("No new upload found.");
}
else {
var dataLength = (data.length - 1);
angular.forEach(data, function(fileData, index) {
$scope.fileUpload(fileData).then(function(response) {
fileUploadStatus[index] = response;
// Check if we are at last element. If yes, then return status
// Return deffered status on last element.
if (dataLength == index) {
deferred.resolve(fileUploadStatus);
}
}, function(response) {
// Check if we are at last element. If yes, then return status
// Return deffered status on last element.
if (dataLength == index) {
deferred.reject(fileUploadStatus);
}
});
});
}
return deferred.promise;
};
The problem is that you have only one promise and are resolving it as soon as the first call uploaded file returns its results.
Save all the promises in an array and then try $q.all:
var promises = [];
angular.forEach(data, function(fileData, index) {
promises.push($scope.fileUpload(fileData));
});
$q.all(promises).then(function(results) {
// loop through the results, one for each promise, and do what you need
})
or just return $q.all(promises) and let the application code handle the results.
The big problem with $q.all is that it just gives and error if ANY of the promises is rejected. If you still want to handle the results for each promise, I use an implementation of $q.allSettled (I think I use this implementation. That returns a response for each promise -- either success or failure -- with the error message or the returned data so that I can then handle the results of each promise separately.
Hope this helps
The updated code in the answer is not working. If the last file is the smallest, it fires the resolve immediately after it's been uploaded. I use instead a fileIsUplaoded counter.
$scope.fileUploadMultiple = function(data) {
var deferred = $q.defer();
var fileUploadStatus = [];
if (angular.isUndefined(data) || angular.isUndefined(data[0])) {
deferred.reject("No new upload found.");
}
else {
var uploadCount = data.length;
angular.forEach(data, function(fileData, index) {
$scope.fileUpload(fileData).then(function(response) {
// if a file is uploaded reduce the uploadCount by 1
uploadCount --;
fileUploadStatus[index] = response;
// Check if we are at last element. If yes, then return status
// Return deffered status on last element.
if (uploadCount == 0) {
deferred.resolve(fileUploadStatus);
}
}, function(response) {
// if a file is not uploaded reduce the uploadCount by 1
uploadCount --;
// Check if we are at last element. If yes, then return status
// Return deffered status on last element.
if (uploadCount == 0) {
deferred.reject(fileUploadStatus);
}
});
});
}
return deferred.promise;
};

$scope var not updating on Parse update

I am building an app using ionic and parse. I am updating a boolean in parse based on a click. Everything works on parse end, I see the user object updated in the console after the function runs, however the scope variable is not updating until user logs out, comes back to the page, and then usually has to even refresh again just to see the $scope.isInstagramLinked updated to its true value.
Controller
var app = angular.module('myApp.controllers.account', []);
app.controller('AccountCtrl', function ($scope, $state, $cordovaOauth, AuthService) {
$scope.isInstagramLinked = AuthService.user.attributes.is_instagram_linked;
$scope.linkInstagram = function() {
$cordovaOauth.instagram('######', [], {})
.then(function(result) {
console.log("Response Object -> " + JSON.stringify(result));
console.log(result.access_token);
// save the access token & get user info
AuthService.setInstagramAccessToken(result.access_token).then(function() {
console.log('Token saved!');
});
}, function(error) {
console.log("Error -> " + error);
});
}
$scope.unlinkInstagram = function() {
AuthService.removeInstagramInfo().then(function() {
console.log('Insta unlinked');
console.log(AuthService.user.attributes);
});
}
});
Service
var app = angular.module('myApp.services.authentication', []);
app.service('AuthService', function ($q, $http, $ionicPopup) {
var self = {
user: Parse.User.current(),
'setInstagramAccessToken': function(token) {
var d = $q.defer();
var user = self.user;
user.set("instagram_access_token", token);
user.save(null, {
success: function(user) {
self.user = Parse.User.current();
d.resolve(self.user);
},
error: function(user, error) {
$ionicPopup.alert({
title: "Save Error",
subTitle: error.message
});
d.reject(error);
}
});
self.setInstagramUserInfo(token);
return d.promise;
},
'setInstagramUserInfo': function(token) {
var d = $q.defer();
var endpoint = 'https://api.instagram.com/v1/users/self?access_token=' + token + '&callback=JSON_CALLBACK';
$http.jsonp(endpoint).then(function(response) {
console.log(response.data.data.username);
console.log(response.data.data.id);
var user = self.user;
user.set('is_instagram_linked', true);
user.set('instagram_username', response.data.data.username);
user.set('instagram_user_id', response.data.data.id);
user.save(null, {
success: function(user) {
self.user = Parse.User.current();
d.resolve(self.user);
},
error: function(user, error) {
$ionicPopup.alert({
title: "Save Error",
subTitle: error.message
});
d.reject(error);
}
});
});
},
'removeInstagramInfo': function() {
var d = $q.defer();
var user = self.user;
user.set('is_instagram_linked', false);
user.set('instagram_access_token', null);
user.set('instagram_username', null);
user.set('instagram_user_id', null);
user.save(null, {
success: function(user) {
self.user = Parse.User.current();
d.resolve(self.user);
},
error: function(user, error) {
$ionicPopup.alert({
title: "Save Error",
subTitle: error.message
});
d.reject(error);
}
});
return d.promise;
}
};
return self;
});
I tried something like this at the end of the function but get an error saying Error: [$rootScope:inprog] $digest already in progress
$scope.$apply(function () {
$scope.isInstagramLinked = false;
});
I'm guessing that you're assuming that the following line
$scope.isInstagramLinked = AuthService.user.attributes.is_instagram_linked;
is going to make '$scope.isInstagramLinked' update anytime 'AuthService.user.attributes.is_instagram_linked' updates. That's not the case, though. Because 'AuthService.user.attributes.is_instagram_linked' references a primitive (boolean) value, it just assigns it - it doesn't maintain any kind of reference to it - that only happens with objects.
You need to manually set $scope.isInstangramLinked = true in the $cordovaOauth.instagram() success/"then" handler.
tl;dr:
$scope.isLinked = false;
someFunction().then(function(){
$scope.isLinked = true; // this is what you're missing
})
.error(function(err){...})
If you don't want to set it manually, you can also use $scope.$watch to watch 'AuthService.user.attributes.is_instagram_linked' for changes, and then update '$scope.isInstagramLinked' when it does.

Jquery file upload autoupload and frontend validation before upload

I use plugin from: https://github.com/blueimp/jQuery-File-Upload
My script loading order is:
jquery.js, angular.js, jquery.ui.widget.js, jquery.iframe-transport.js, jquery.fileupload.js, jquery.fileupload-process.js, jquery.fileupload-validate.js, jquery.fileupload-angular.js
My controller is:
$scope.profileUploadOptions = {
url: API_URL + "images/user",
maxFileSize: 50,
acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
forceIframeTransport: false,
add: function(e, data) {
$.each(data.files, function (index, file) {
console.log("Added file: " + file.name);
});
console.log(data);
},
start: function(e) {
console.log("Uploads started");
},
stop: function (e) {
console.log("Uploads finished");
},
change: function (e, data) {
$.each(data.files, function (index, file) {
console.log("Changed file: " + file.name);
});
},
progress: function(e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
console.log("Progress: " + progress);
},
progressAll: function (e, data) {
var progress = parseInt(data.loaded / data.total * 100, 10);
console.log("Progress: " + progress);
},
done: function(e, data) {
console.log("Done: ");
console.log(data);
},
fail: function(e, data) {
console.log("Fail: ");
console.log(data);
},
always: function(e, data) {
console.log("Always: ");
console.log(data);
}
};
How i can submit upload after browse single file from browse window, and where i can handle errors if file type not in acceptFileTypes Regex, or is bigger than maxFileSize?
The plugin documentation should answer your question on auto-uploading, and there are a host of examples that are easy enough to find regarding setting up the fileinput piece of your question.
As far as validating files before submitting an upload, this posed answer, pasted below, is a good starting point.
$fileInput.fileupload({
url: 'upload_url',
type: 'POST',
dataType: 'json',
autoUpload: false,
disableValidation: false,
maxFileSize: 1024 * 1024,
messages: {
maxFileSize: 'File exceeds maximum allowed size of 1MB',
}
});
$fileInput.on('fileuploadadd', function(evt, data) {
var $this = $(this);
var validation = data.process(function () {
return $this.fileupload('process', data);
});
validation.done(function() {
makeAjaxCall('some_other_url', { fileName: data.files[0].name, fileSizeInBytes: data.files[0].size })
.done(function(resp) {
data.formData = data.formData || {};
data.formData.someData = resp.SomeData;
data.submit();
});
});
validation.fail(function(data) {
console.log('Upload error: ' + data.files[0].error);
});
});
Using the other author's code, the providing user feedback can be achieved in the validation.fail handler.

Resources