Ionic Photo Capture & Crop - angularjs

I have a basic ionic application, i'd like the app to take a photo of the user, and the user can then crop the taken photo to a passport sized photo.
Does anybody know how I can achieve such a thing? I've tried jrcrop but for the life of me I can't get it working.

For my Ionic app, I used a combination of ng-flow for the upload (on the flow-file-added, validate the file is OK (met file extension/upload requirements etc.)) then initiated an instance of ngCropper to perform the cropping. Once cropping is complete, initiated the flow.upload() on the flowjs object to perform the upload.
It's not possible to provide all my code to this solution, but the real stitching to make this happen occurs after cropping is:
First, retrieve the data URL of the cropped canvas, via a command like var dataUrl = this.$cropCanvas.cropper('getCroppedCanvas').toDataURL();
Create a blob from it, something like this JS function works well.
Remove the original queued upload file (the full image)
Replace it with the cropped blob
Upload.
The replace and upload technique looks like this:
var theBlob = that.dataURLToBlob(dataUrl);
theBlob.name = Utility.generateGuid() + '.jpg'; // give it a new name if you like
// Remove existing image which was added to flow files cache on image dialog select
$scope.flowTileObj.flow.removeFile($scope.flowTileObj.flow.files[0]);
$scope.flowTileObj.flow.addFile(theBlob);
// Perform upload
$scope.flowTileObj.flow.upload();
Best of luck.

U need to add this plugin
bower install --save ngCordova
cordova plugin add cordova-plugin-camera
cordova plugin add cordova-plugin-file
ionic platform add ios
And use this code
<img ng-repeat="image in images" ng-src="{{urlForImage(image)}}" height="200px"/>
$scope.addImage = function() {
var options = {
destinationType : Camera.DestinationType.FILE_URI,
sourceType : Camera.PictureSourceType.CAMERA, // Camera.PictureSourceType.PHOTOLIBRARY
allowEdit : false,
encodingType: Camera.EncodingType.JPEG,
popoverOptions: CameraPopoverOptions,
};
$cordovaCamera.getPicture(options).then(function(imageData) {
onImageSuccess(imageData);
function onImageSuccess(fileURI) {
createFileEntry(fileURI);
}
function createFileEntry(fileURI) {
window.resolveLocalFileSystemURL(fileURI, copyFile, fail);
}
function copyFile(fileEntry) {
var name = fileEntry.fullPath.substr(fileEntry.fullPath.lastIndexOf('/') + 1);
var newName = makeid() + name;
window.resolveLocalFileSystemURL(cordova.file.dataDirectory, function(fileSystem2) {
fileEntry.copyTo(
fileSystem2,
newName,
onCopySuccess,
fail
);
},
fail);
}
function onCopySuccess(entry) {
$scope.$apply(function () {
$scope.images.push(entry.nativeURL);
});
}
function fail(error) {
console.log("fail: " + error.code);
}
function makeid() {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i=0; i < 5; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
}, function(err) {
console.log(err);
});
}

Related

Cordova camera plugin: Why aren't my CameraOptions being picked up?

My code looks something like this (using angularjs):
angular.module('myApp').factory('photoService', function(...){
var camera = navigator.camera;
var cameraOpts = { destinationType: camera.DestinationType.DATA_URL };
cameraService.takePhoto = function(successCallback, failureCallback){
console.log(JSON.stringify(cameraOpts));
camera.getPicture(function(img){
console.log(img);
//handle success
}, function(e){
console.log(e);
//handle failure
}, cameraOptions);
}
});
My cameraOpts looks correctly populated, since its log shows {"destinationType":0} (i.e. destinationType != undefined, so it was correctly pulled from the camera object). My issue is that when I do console.log(img), I am still getting a file URI instead of the image as a base64 string. Why is this?

Load images as a service

I have service that pulls an object from an API. Some of that object may contain image URLs. The backend currently scans for these, and processes them, (in PHP) by get_file_contents() and translating them to inline data. This is heavily loading the throughput on my server. The reason I am doing this is because I want to cache the images for being offline later, but in a way that I can still just use regular angular to render the object.
I can't do the processing in Javascript in the browser with $http.get() because the site hosting the images is blocking the cross-site request. What I thought to do, then, was to create an <IMG> element in the browser, that called the service back once it was loaded so I can extract the data and process the object with it.
I can't control the service worker to store the get from inside the app, and the URL's are not known by the app at any time before it downloads the API object anyway.
I did think about redoing the service worker to store gets from off my site as well, but that seemed a little bit wrong, and I'm not sure how well it would work anyway, plus, while developing, I switch off the service worker as it means I have to let the entire site load twice for it to refresh completely.
Can anyone help me with a way to get image data via the browser into my service?
If I had found a CORS supportive image host in the first place I may not have needed this and could probably have just used the $http call.
A directive, service and controller are required, as well as a host that supports CORS (Imgur for example). I also used this base64 canvas code.
Here is the javascript code:
// Using this from https://stackoverflow.com/questions/934012/get-image-data-in-javascript
function getBase64Image(img) {
// Create an empty canvas element
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
// Copy the image contents to the canvas
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// Get the data-URL formatted image
// Firefox supports PNG and JPEG. You could check img.src to
// guess the original format, but be aware the using "image/jpg"
// will re-encode the image.
var dataURL = canvas.toDataURL("image/png");
return dataURL;
// return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}
// Used on the img tag to handle the DOM notification feeding into the service
app.directive('notifyimgsvc', function() {
return {restrict : 'A', link : function(scope, element, attrs) {
element.bind('load', function() {
console.log('imgSvc::notify() image is loaded');
console.log("imgSvc::notify(): " + this.src);
imgSvc.notifyLoad(this.src, getBase64Image(this));
});
element.bind('error', function() {
console.log('imgSvc::notify() image could not be loaded');
console.log("imgSvc::notify(): " + this.src);
});
}};
});
// A core service to handle the comms in both directions from requests to data
app.service('imgSvc', [function(netSvc) {
imgSvc = this; // to avoid ambiguoity in some inner function calls
imgSvc.images = {}; // a cache of images
imgSvc.requests = []; // the requests and their callbacks
imgSvc.handlers = []; // handlers that will render images
console.log("imgSvc::init()");
// Allows a controller to be notified of a request for an image and
// a callback to call when an image is added. There should only ever
// be one of these so an array is probaby not needed and any further
// requests should probably throw an error.
imgSvc.registerHandler = function(callback) {
console.log("imgSvc::registerHandler()");
if (imgSvc.requests.length) {
// Already have image requests, so tell the new handler about them
for ( var i in imgSvc.requests) {
callback(imgSvc.requests[i].url);
}
}
// Add the new handler to the stack
imgSvc.handlers.push(callback);
};
// The usage function from your code, provide a callback to get notified
// of the data when it loads.
imgSvc.getImg = function(url, callback) {
console.log("imgSvc::getImg('" + url + "')");
// If we have pre-cached it, send it back immediately.
if (imgSvc.images[url] != undefined) {
console.log("imgSvc::getImg('" + url + "'): Already have data for this one");
callback(url, imgSvc.images[url]);
return;
}
// push an object into the request queue so we can process returned data later.
// Doing it this way als means you can have multiple requests before any data
// is returned and they all get notified easily just by looping through the array.
var obj = {"url" : url, "callback" : callback};
if (imgSvc.handlers.length) {
console.log("imgSvc::getImg('" + url + "'): informing handler");
for ( var i in imgSvc.handlers) {
imgSvc.handlers[i](obj.url);
}
}
imgSvc.requests.push(obj);
};
// Notification of a successful load (or fail if src == null).
imgSvc.notifyLoad = function(url, src) {
console.log("imgSvc.notifyLoad()");
// Save the data to the cache so any further calls can be handled
// immediately without a request being created.
imgSvc.images[url] = src;
// Go though the requests list and call any callbacks that are registered.
if (imgSvc.requests.length) {
console.log("imgSvc.notifyLoadCallback('" + url + "'): scanning requests");
for (var i = 0; i < imgSvc.requests.length; i++) {
if (imgSvc.requests[i].url == url) {
console.log("imgSvc.notifyLoadCallback('" + url + "'): found request");
// found the request so remove it from the request list and call it
var req = imgSvc.requests.splice(i, 1)[0];
i = i - 1;
console.log("imgSvc.notifyLoadCallback('" + url + "')");
req.callback(url, src);
} else {
console.log("imgSvc.notifyLoadCallback('" + url + "'): skipping request for '" + imgSvc.requests[i].url + "'");
}
}
} else {
console.log("imgSvc.notifyLoadCallback('" + url + "'): No requests present??");
}
};
// The notifiy fail is just a logging wrapper around the failure.
imgSvc.notifyFail = function(url) {
console.log("imgSvc.notifyFail()");
imgSvc.notifyLoad(url, null);
};
}]);
// A simple controller to handle the browser loading of images.
// Could probably generate the HTML, but just doing simply here.
app.controller('ImageSvcCtrl', ["$scope", function($scope) {
$scope.images = [];
console.log("imgSvcCtrl::init()");
// Register this handler so as images are pushed to the service,
// this controller can render them using regular angular.
imgSvc.registerHandler(function(url) {
console.log("imgSvcCtrl::addUrlHandler('" + url + "')");
// Only add it if we don't hqve it already. The caching in the
// service will handle multiple request for the same URL, and
// all associated malarkey
if ($scope.images.indexOf(url) == -1) {
$scope.images.push(url);
}
});
}]);
The HTML you need for this is very simple:
<div data-ng-controller="ImageSvcCtrl" style="display:none;">
<img data-ng-repeat="img in images" data-ng-src="{{img}}" alt="loading image" crossorigin="anonymous" notifyimgsvc />
</div>
And you call it within your controllers like this:
var req_url = "https://i.imgur.com/lsRhmIp.jpg";
imgSvc.getImg(req_url, function(url, data) {
if(data) {
logger("MyCtrl.notify('" + url + "')");
} else {
logger("MyCtrl.notifyFailed('" + url + "')");
}
});

Download response as an excel file

File is not downloading at browser. I'm preparing the file and writing it to output stream of response.
Rest API is there:
#RequestMapping(value = "/export-companies",
method = {RequestMethod.GET, RequestMethod.HEAD})
#Timed
public void downloadCompanies(HttpServletResponse response) throws URISyntaxException {
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("Sample sheet");
Map<String, Object[]> data = new HashMap<String, Object[]>();
data.put("1", new Object[] {"Emp No.", "Name", "Salary"});
data.put("2", new Object[] {1d, "John", 1500000d});
data.put("3", new Object[] {2d, "Sam", 800000d});
data.put("4", new Object[] {3d, "Dean", 700000d});
Set<String> keyset = data.keySet();
int rownum = 0;
for (String key : keyset) {
Row row = sheet.createRow(rownum++);
Object [] objArr = data.get(key);
int cellnum = 0;
for (Object obj : objArr) {
Cell cell = row.createCell(cellnum++);
if(obj instanceof Date)
cell.setCellValue((Date)obj);
else if(obj instanceof Boolean)
cell.setCellValue((Boolean)obj);
else if(obj instanceof String)
cell.setCellValue((String)obj);
else if(obj instanceof Double)
cell.setCellValue((Double)obj);
}
}
try {
ByteArrayOutputStream outByteStream = new ByteArrayOutputStream();
workbook.write(outByteStream);
byte [] outArray = outByteStream.toByteArray();
response.setContentType("application/ms-excel");
response.setContentLength(outArray.length);
response.setHeader("Expires:", "0"); // eliminates browser caching
response.setHeader("Content-Disposition", "attachment; filename=template.xls");
OutputStream outStream = response.getOutputStream();
outStream.write(outArray);
outStream.flush();
workbook.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
From front end (using Angular JS):
(function() {
'use strict';
angular
.module('MyApp')
.factory('CompanyExportService', CompanyExportService);
CompanyExportService.$inject = ['$resource'];
function CompanyExportService ($resource) {
var service = $resource('api/export-companies', {}, {
'get': {
method: 'GET',
isArray: false
}
});
return service;
}
})();
File contents are there in response as non-readable format. But file is not downloaded at browser.
Angular will receive the file contents mere character sequences. You need to create a file from these characters and initiate the browser download in frontend.
You can do it like this -
var blob = new Blob([data],
{type: 'application/vnd.openxmlformat-officedocument.spreadsheetml.sheet;'});
saveAs(blob, fileName);
where data is the response you received form your API. The saveAs function is part of FileSaver.js library. Although you can look on how to manually do that but why reinvent the wheel?
Downloading files with XHR is problematic. As long as you do only GET requests, there exists much simpler approach to trigger browser to download file.
Use JavaScript native method window.open(url).
It does work well in all browsers including IE9.
In code below, I use $window, which is Angular's proxy for native window object.
Example for your code could be like:
(function() {
'use strict';
angular
.module('MyApp')
.factory('CompanyExportService', CompanyExportService);
CompanyExportService.$inject = ['$window'];
function CompanyExportService ($window) {
var exportUrl = 'api/export-companies';
return {
download: download
}
function download() {
$window.open(exportUrl);
}
}
})();
Note that this action is out of scope of Angular, you can't do much about error handling or waiting till the file will be downloaded. Might be problem if you want to generate huge Excel files or your API is slow.
For more details, read question: Spring - download response as a file
Update:
I've replaced window.location.href with window.open() which seems to be better choice for downloading files.
If your API will throw an error page instead of file, window.location.href will replace current page (thus losing its state). $window.open() however will opens this error in new tab without losing current state of of application.
You can download file in new tab. Modern browser are closing them automatically when downloading is completed.
By opening new window you get reference to it, when downloading is completed then window.closed is set to true.
Unfortunatelly you need to check from time-to-time this param inside interval ...
var newWindowRef = $window.open(url, name);
if (newWindowRef) {
if (newWindowRef.document.body) { // not working on IE
newWindowRef.document.title = "Downloading ...";
newWindowRef.document.body.innerHTML = '<h4>Your file is generating ... please wait</h4>';
}
var interval = setInterval(function() {
if (!!newWindowRef.closed) {
// Downloading completed
clearInterval(interval);
}
}, 1000);
} else {
$log.error("Opening new window is probably blocked");
}
Tested and works on Chrome v52, FF v48 and IE 11

How to upload an image to FTP without mulitpart only on FTP client

I am trying to upload an image to my FTP what i have done so far is i have successfully invoked the image and i have the image location in
file:///storage/sdcard0/Android/data/com.ionicframework.ftptranfer949961/cache/1467013143014.png
i have to send the .png image file to my FTP
var app = angular.module('starter', ['ionic','ngCordova'])
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
if(window.cordova && window.cordova.plugins.Keyboard) {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
// Don't remove this line unless you know what you are doing. It stops the viewport
// from snapping when text inputs are focused. Ionic handles this internally for
// a much nicer keyboard experience.
cordova.plugins.Keyboard.disableScroll(true);
}
if(window.StatusBar) {
StatusBar.styleDefault();
}
});
})
app.service('MyService',function(){
var imgPath = [];
//var imgName = [];
this.getImgPath = function(){
return imgPath;
}
this.setImgPath = function(path){
console.log(path);
imgPath = path ;
/*this.getImgName = function(){
return imgName;
}
this.setImgName = function(pass){
console.log(pass);
imgName = pass;
}*/
}
});
//https://github.com/xfally/cordova-plugin-ftp
app.controller('mycontroller',function($scope,$cordovaCamera,$cordovaFileTransfer,MyService,$log,$timeout,$window){
$scope.takePicture = function(){
var options = {
quality : 75,
destinationType: Camera.DestinationType.FILE_URI,
sourceType: Camera.PictureSourceType.CAMERA,
allowEdit : false,
encodingType: Camera.EncodingType.PNG,
mediaType: Camera.MediaType.PICTURE,
targetWidth: 250,
targetHeight: 250,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: true
};
$cordovaCamera.getPicture(options).then(function(imageURI) {
console.log('invokeing cordovaCamera');
$scope.image = imageURI;
// imageURI.substr(imageURI.lastIndexOf('/')+1);
console.log(imageURI.substr(imageURI.lastIndexOf('/')+1));
MyService.setImgPath(imageURI);
console.log($scope.image);
console.log(imageURI);
$scope.upload();
}, function(err) {
// An error occured. Show a message to the user
console.log(err);
});
};
$scope.upload =function(){
var ping = MyService.getImgPath().substr(MyService.getImgPath().lastIndexOf('/')+1);
console.log(ping);
// Test code (for angularjs)
// Tip: Usually init/create $window.cordova.plugin.ftp will take some time, so set a `timeout()` to make sure it's ready.
// But surely, the best and safest way is to listen `deviceready` event for cordova, or `$ionicPlatform.ready()` for ionic.
// You can find more info in official docs of cordova or ionic.
$timeout(function() {
if ($window.cordova.plugin.ftp) {
$log.log("xtest: ftp: found");
// 1. connect to one ftp server, then you can do any actions/cmds
$window.cordova.plugin.ftp.connect("308.3d8.myftpupload.com", "wepopusers", "Chandru#123", function() {
$log.log("xtest: ftp: connect ok");
// 2. list one dir, note that just can be dir, not file
$window.cordova.plugin.ftp.ls("/gopi", function(fileList) {
$log.log("xtest: ftp: list ok");
if (fileList && fileList.length > 0) {
$log.log("xtest: ftp: The last file'name is " + fileList[fileList.length - 1].name);
$log.log("xtest: ftp: The last file'type is " + fileList[fileList.length - 1].type);
$log.log("xtest: ftp: The last file'link is " + fileList[fileList.length - 1].link);
$log.log("xtest: ftp: The last file'size is " + fileList[fileList.length - 1].size);
$log.log("xtest: ftp: The last file'modifiedDate is " + fileList[fileList.length - 1].modifiedDate);
// 4. upload local file to remote, you can rename at the same time. arg1: local file, arg2: remote file.
// make sure you can access and read the local file.
$window.cordova.plugin.ftp.upload("/" + ping, "/gopi/" + ping, function(percent) {
console.log(percent);
if (percent == 1) {
$log.log("xtest: ftp: upload finish");
} else {
$log.log("xtest: ftp: upload percent=" + percent*100 + "%");
}
}, function(error) {
$log.log("xtest: ftp: upload error=" + error);
});
}
}, function(error) {
$log.log("xtest: ftp: list error: " + error);
});
});
} else {
$log.log("xtest: ftp: not found!");
}
}, 2000);
};
});
you can see in this line
$window.cordova.plugin.ftp.upload("/ping", "/gopi/ping", function(percent)
/ping in this i have the image name 1467013143014.png
but i am getting the error as
xtest: ftp: upload error=java.io.FileNotFoundException: /ping
For more details look this git-hub cordova-ftp .
i have successfully uploaded one file named default.prop to my FTP but i dont know how it is uploaded to my ftp other then any thing what i have given to my FTP i am not able to upload it
I have even hot code my path of the image in my upload area but not able to upload an image.
how can i access the file of my image and upload to my FTP
First thing i notice is that your ping variable holds the file path. Hence you cannot use "/ping" as it will be treated as a string not variable. Try "/" + ping.
Also your ping variable just holds the file path not name. So try using "/" + ping + FILE_NAME
To avoid hard coding the path, try using cordova file plugin. In your case, after installing the plugin, try using cordova.file.applicationDirectory or cordova.file.applicationStorageDirectory which will give you the path you are looking to harcode. More info on file path is available in the official file plugin link. Hope it helps.

Read barcode via camera in an ASP.NET MVC 5 Application

I have created a web site with ASP.NET MVC 5. This web site is also available on mobile devices as a web app. But now I want to add the possibility for the user to scan barcodes with the mobile camera when they are using the app on their mobiles. Of course there are tools like phonegap that enable read barcodes, but the point is I want to add this functionality in my ASP.NET MVC 5 project.
So is there a way to read barcodes via the mobile camera in ASP.NET MVC 5?
I have solved this issue and here is the solution:
In the view (Index.chtml):
<form>
<input type="file" class="upload" size="45" name="file" id="file">
</form>
It is important to write the <input type="file"...> in a form tag.
Next I use the javascript. I use it because I want to call the controller as soon as the Browse button is clicked. You can use a submit button, too.
Javascript:
$('#file').on("change", function () {
for (i = 0; i < $('form').length; i++) {
if ($('form').get(i)[0].value != "") /* get the file tag, you have to customize this code */
{
var formdata = new FormData($('form').get(i));
CallService(formdata);
break;
}
}
});
function CallService(file) {
$.ajax({
url: '#Url.Action("Scan", "Home")',
type: 'POST',
data: file,
cache: false,
processData: false,
contentType: false,
success: function (barcode) {
alert(barcode);
},
error: function () {
alert("ERROR");
}
});
}
Next we have have analyse the image in the server and read the barcode of it. I am using the Aspose.BarCode Library:
HomeController.cs
public JsonResult Scan(HttpPostedFileBase file)
{
string barcode = "";
try
{
string path = "";
if (file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
path = Path.Combine(Server.MapPath("~/App_Data"), fileName);
file.SaveAs(path);
}
// Now we try to read the barcode
// Instantiate BarCodeReader object
BarCodeReader reader = new BarCodeReader(path, BarCodeReadType.Code39Standard);
System.Drawing.Image img = System.Drawing.Image.FromFile(path);
System.Diagnostics.Debug.WriteLine("Width:" + img.Width + " - Height:" + img.Height);
try
{
// read Code39 bar code
while (reader.Read())
{
// detect bar code orientation
ViewBag.Title = reader.GetCodeText();
barcode = reader.GetCodeText();
}
reader.Close();
}
catch (Exception exp)
{
System.Console.Write(exp.Message);
}
}
catch (Exception ex)
{
ViewBag.Title = ex.Message;
}
return Json(barcode);
}
}
Now the decoded barcode is returned to the view.

Resources