Ionic/Cordova. Save image to photo gallery - angularjs

I have a URLs to external PNG images. I want to download it directly to Camera Roll (iOS) or Photo Gallery(Android). How I can manage it with Ionic

try this:
cordova plugin add org.apache.cordova.file-transfer
cordova plugin add cordova-plugin-file
add file link in index.
<script src="js/ng-cordova.js"></script>
<script src="js/FileTransferController.js"></script>
in controller:
$scope.downloadFile = function() {
var url = "http://your_ip_address/images/my.png";
var filename = url.split("/").pop();
alert(filename);
var targetPath = cordova.file.externalRootDirectory + filename;
var trustHosts = true
var options = {};
alert(cordova.file.externalRootDirectory);
$cordovaFileTransfer.download(url, targetPath, options, trustHosts)
.then(function(result) {
// Success!
alert(JSON.stringify(result));
}, function(error) {
// Error
alert(JSON.stringify(error));
}, function (progress) {
$timeout(function () {
$scope.downloadProgress = (progress.loaded / progress.total) * 100;
})
});
}
From HERE
hope its helpful to someone.

Related

call function synchronously inside an angular for each

I'm using ngCordova File Transfer plugin in an ionic project to download set of images from urls. Here is the code i'm using for that.
// Save a image file in a given directory
$scope.saveImage = function(dir,imgUrl,imageName) {
var url = imgUrl;
var targetPath = cordova.file.dataDirectory+ dir+"/" + imageName;
var trustHosts = true;
var options = {};
// Download the image using cordovafiletransfer plugin
$cordovaFileTransfer.download(url, targetPath, options, trustHosts)
.then(function(result) {
$scope.loadedCount ++;
$ionicLoading.show({template : "<ion-spinner class='spinner-energized'></ion-spinner><p> Downloading pages : "+ $scope.loadedCount+" of "+ $scope.pages.length+ "</p><p>Please wait...</p><p><button class=\"button button-block button-positive\">continue in background</button></p>"});
if($scope.loadedCount == $scope.pages.length) {
$ionicLoading.hide();
$scope.showDownloadSuccessAlert = function() {
var alertPopup = $ionicPopup.alert({
title: 'Success!',
template: "Your magazine successfully downloaded. You can view it on Downloads!"
});
};
$scope.showDownloadSuccessAlert();
}
}, function(err) {
alert(JSON.stringify(err));
}, function (progress) {
if($scope.loadedCount > 80) {
}
});
};
// Download the current magazine
$scope.downloadMagazine = function() {
if($rootScope.user.user_id == undefined) {
$scope.showLoginAlert = function() {
var alertPopup = $ionicPopup.alert({
title: 'Oops!',
template: "Your must login to download magazines"
});
};
$scope.showLoginAlert();
return;
}
document.addEventListener('deviceready', function () {
var dirName = $rootScope.currentIssue.slug+'_VOL_'+$rootScope.currentIssue.vol+'_ISU_'+$rootScope.currentIssue.issue;
// First create the directory
$cordovaFile.createDir(cordova.file.dataDirectory, dirName, false)
.then(function (success) {
var count = 1;
$scope.loadedCount = 0;
angular.forEach($scope.pages, function(value, key) {
var imgName = count+".png";
$scope.saveImage(dirName,value.link,imgName); // Then save images one by one to the created directory.
count++;
});
}, function (error) {
// Directory already exists means that the magazine is already downloaded.
$scope.showDownloadedAlert = function() {
var alertPopup = $ionicPopup.alert({
title: 'Why worry!',
template: "Your have already downloaded this magazine. You can view it on downloads"
});
};
$scope.showDownloadedAlert();
});
}, false);
};
})
Problem here is that program try to download everything at once without waiting for previous one to finish. How to wait for one file to finish downloading and then start the other?
Thanks
If you want to do that automatically (you're not the first one : How can I execute array of promises in sequential order?), you could try reducing the list of address to a single Promise that will do the whole chain.
$scope.pages.reduce(function (curr,next) {
return curr.then(function(){
return $scope.saveImage(dirName, curr.link, imgName);
});
}, Promise.resolve()).then(function(result) {
$ionicLoading.show({template : "<ion-spinner class='spinner-energized'></ion-spinner><p> Downloading pages : "+ $scope.loadedCount+" of "+ $scope.pages.length+ "</p><p>Please wait...</p><p><button class=\"button button-block button-positive\">continue in background</button></p>"});
if($scope.loadedCount == $scope.pages.length) {
$ionicLoading.hide();
$scope.showDownloadSuccessAlert = function() {
var alertPopup = $ionicPopup.alert({
title: 'Success!',
template: "Your magazine successfully downloaded. You can view it on Downloads!"
});
};
$scope.showDownloadSuccessAlert();
}
});
And don't forget to make your saveImage async which returns a promise.
UPDATE:
You will need to remove the then logic from your save method and return the download method call:
return $cordovaFileTransfer.download(url, targetPath, options, trustHosts).promise;
Then you can put your download handler into Promise.resolve()).then. See above.
There's no other way other than chaining your promises. Here's an example:
angular.module('app', [])
.service('fakeDownloadService', function($timeout) {
this.download = (file) => $timeout(() => file, 100);
return this;
})
.run(function($rootScope, $q, fakeDownloadService) {
var listOfFiles = [];
for (var i = 0; i < 10; i++)
listOfFiles.push('file' + i);
$rootScope.log = [];
$rootScope.download = () => {
listOfFiles
.reduce((prev, curr) => {
return prev.then((result) => {
if(result)
$rootScope.log.push(result + ' downloaded');
return fakeDownloadService.download(curr);
});
}, $q.resolve())
.then(() => $rootScope.log.push('all done'));
};
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.9/angular.min.js"></script>
<div ng-app="app">
<button ng-click="download()">Start</button>
<div>Log:</div>
<ul>
<li ng-repeat="entry in log track by $index">
{{entry}}
</li>
</ul>
</div>

Collect Images from Local Server(Acquia Dev Desktop)

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" />

AngularJS Display Blob in embed tag - Document not loaded

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:

Force image orientation angularjs

I have this odd behavior when I upload an image and if this image size has more height than with I get the image rotated 90 degrees.
check this fiddle that's using ngImgCrop and this is the image that I'm uploading
the code of the ngDmgCrop it's pretty standard:
angular.module('app', ['ngImgCrop'])
.controller('Ctrl', function($scope) {
$scope.myImage='';
$scope.myCroppedImage='';
var handleFileSelect=function(evt) {
var file=evt.currentTarget.files[0];
var reader = new FileReader();
reader.onload = function (evt) {
$scope.$apply(function($scope){
$scope.myImage=evt.target.result;
});
};
reader.readAsDataURL(file);
};
angular.element(document.querySelector('#fileInput')).on('change',handleFileSelect);
});
how can I fix this behavior?
You'll have to parse the exif data in the image header, examine the Orientation tag, and rotate accordingly.
I just solved the same problem with this library: Javascript Load Image
In your app.js
var handleFileSelect = function(evt) {
var target = evt.dataTransfer || evt.target;
var file = target && target.files && target.files[0];
var options = {canvas:true};
var displayImg = function(img) {
$scope.$apply(function($scope){
$scope.myImage=img.toDataURL();
});
}
loadImage.parseMetaData(file, function (data) {
if (data.exif) {
options.orientation = data.exif.get('Orientation');
}
loadImage(file, displayImg, options );
});
};
Demo : Plunker
Cheers.

$http upload file progress in AngularJS

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);
})
}

Resources