how to convert image into base64 string? - angularjs

hello I am trying to make simple application in ionic using camera or select file from gallery or file system and send to / or upload to a server.
I found one plugin which capture one image and send base64 string
here is plugin
http://ngcordova.com/docs/plugins/camera/
using this I am able to get base64 string
$cordovaCamera.getPicture(options).then(function(imageData) {
var image = document.getElementById('myImage');
image.src = "data:image/jpeg;base64," + imageData;
}, function(err) {
// error
});
that base64 string I used to send to server
But my problem is that when I use this plugin image gallary plugin
http://ngcordova.com/docs/plugins/imagePicker/
it send me only image url (where is in memory) .can we get base64 string after selecting the image from image picker.
$cordovaImagePicker.getPictures(options)
.then(function (results) {
for (var i = 0; i < results.length; i++) {
console.log('Image URI: ' + results[i]);
}
}, function(error) {
// error getting photos
});
In other words when I using camera I am getting base64 string as shown above.But when I use image picker plugin I am getting image url.can I get base64 string .so that I am able to send or upload on server .
how to convert image url to base64 string ?

Try this 100% working code. First you have to Download ngCordova.min.js file and include it in your index.html file. Follow this code.
angular.module('starter.controllers', [])
.controller('DashCtrl', function($scope,$cordovaCamera) {
$scope.imgUrl;
$scope.dataImg;
$scope.tackPicture = function(){
var options = {
quality: 50,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.CAMERA,
allowEdit: true,
encodingType: Camera.EncodingType.JPEG,
targetWidth: 100,
targetHeight: 100,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false,
correctOrientation:true
};
$cordovaCamera.getPicture(options).then(function(imageData) {
//var image = document.getElementById('myImage');
$scope.dataImg = imageData; // <--- this is your Base64 string
$scope.imgUrl = "data:image/jpeg;base64," + imageData;
}, function(err) {
// error
});
}
})
.controller('ChatsCtrl', function($scope, Chats) {
// With the new view caching in Ionic, Controllers are only called
// when they are recreated or on app start, instead of every page change.
// To listen for when this page is active (for example, to refresh data),
// listen for the $ionicView.enter event:
//
//$scope.$on('$ionicView.enter', function(e) {
//});
$scope.chats = Chats.all();
$scope.remove = function(chat) {
Chats.remove(chat);
};
})
.controller('ChatDetailCtrl', function($scope, $stateParams, Chats) {
$scope.chat = Chats.get($stateParams.chatId);
})
.controller('AccountCtrl', function($scope) {
$scope.settings = {
enableFriends: true
};
});
<ion-view view-title="Dashboard">
<ion-content class="padding">
<button class="button icon-left ion-ios-camera button-block button-positive" ng-click="tackPicture()">
OPEN CAMERA
</button>
<div class="item item-avatar">
<img ng-src="{{ imgUrl }}">
<p>{{ dataImg }}</p>
</div>
</ion-content>
</ion-view>

To convert image to base64 you can use HTML5 canvans as mention in
How to convert image into base64 string using javascript
Please refer above question
Use this code
/**
* Convert an image
* to a base64 url
* #param {String} url
* #param {Function} callback
* #param {String} [outputFormat=image/png]
*/
function convertImgToBase64URL(url, callback, outputFormat){
var img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function(){
var canvas = document.createElement('CANVAS'),
ctx = canvas.getContext('2d'), dataURL;
canvas.height = this.height;
canvas.width = this.width;
ctx.drawImage(this, 0, 0);
dataURL = canvas.toDataURL(outputFormat);
callback(dataURL);
canvas = null;
};
img.src = url;
}

Related

Ionic Photo Upload - File to Base64 String

I am working on an Ionic App that is communicating with a rails API. I have users, and user have pictures. I have been able to follow this guide about how to allow users to grab images natively from their phone images.
this allows the user to grab an image from their phone
$ionicPlatform.ready(function() {
$scope.getImageSaveContact = function() {
// Image picker will load images according to these settings
var options = {
maximumImagesCount: 1,
width: 800,
height: 800,
quality: 80
};
$cordovaImagePicker.getPictures(options).then(function (results) {
// Loop through acquired images
for (var i = 0; i < results.length; i++) {
$scope.collection.selectedImage = results[i]; // We loading only one image so we can use it like this
window.plugins.Base64.encodeFile($scope.collection.selectedImage, function(base64){ // Encode URI to Base64 needed for contacts plugin
$scope.collection.selectedImage = base64;
});
}
console.log("results");
console.log(results);
}, function(error) {
console.log('Error: ' + JSON.stringify(error));
});
};
});
The problem is, it is not running (or appears not to not be running) the window.plugins.Base64.encodeFile line that encodes a file. Right now, it is only the image file and not the Base64 encoded string.
How do I get this function to run, after I have grabbed a file from my device camera?
i was able to figure it out, answer is below
from an old project https://github.com/aaronksaunders/firebaseStorageApp/blob/master/www/js/app.js#L132
return $cordovaFile.readAsArrayBuffer(path, fileName)
.then(function (success) {
// success - get blob data
var imageBlob = new Blob([success], { type: "image/jpeg" });
})
add this camera plugin
cordova plugin add cordova-plugin-camera
this returns image in base 64 by default..
$scope.choosePhoto = function () {
$scope.myPopup.close();
var options = {
quality: 75,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
allowEdit: true,
encodingType: Camera.EncodingType.JPEG,
targetWidth: 300,
targetHeight: 300,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false
};
$cordovaCamera.getPicture(options).then(function (imageData) {
$scope.imgURI = "data:image/jpeg;base64," + imageData;
}, function (err) {
// An error occured. Show a message to the user
});
}
more details can be found here
http://ngcordova.com/docs/plugins/camera/
hope this helps...
I was able to figure this out by piecing together a bunch of stuff, especially w/ the rails side. The idea is to click a button to get an image, pick one from your camera roll, convert that image to a base64 string, then send that image to the server.
my current stack is rails 4, ionic/angular v1. hopefully this helps someone else.
angular controller
function toDataUrl(url, callback) {
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var reader = new FileReader();
reader.onloadend = function() {
callback(reader.result);
}
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.send();
}
$scope.grabImage = function () {
var options = {
maximumImagesCount: 1,
width: 800,
height: 800,
quality: 80
};
$cordovaImagePicker.getPictures(options).then(function (results) {
$scope.dataImg = results;
return toDataUrl($scope.dataImg, function(base64Img) {
$scope.base64 = base64Img;
$state.go($state.current, {}, {reload: false});
})
}, function(error) {
$scope.message = "Error: Failed to Attach Image";
var alertPopup = $ionicPopup.alert({
title: 'User Photos',
templateUrl: 'templates/modals/success_or_error.html',
scope: $scope
});
});
}
rails controller
def create
image = Paperclip.io_adapters.for(params[:image_file])
image.class.class_eval { attr_accessor :original_filename, :content_type }
image.original_filename = "mu_#{#current_mobile_user.id}_#{#current_mobile_user.pictures.count}.jpg"
image.content_type = "image/jpeg"
#picture = #current_mobile_user.pictures.create(image: image, imageable_id: #current_mobile_user.id)
if #picture.save
render json: ['Picture Uploaded!'], status: :created
else
render json: [#picture.errors.full_messages.to_sentence], status: :unprocessable_entity
end
end

getting Blob:URL from $cordovaCamera, Not valid base64 string

I am working on cordova mobile application, I need to upload an Image on server. I want to send base64 string to my webapi and there I can convert base64 to Image and save image on server. I am doing this $cordovaCamera in controller as
$scope.takePicture = function () {
var options = {
quality: 75,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.CAMERA,
allowEdit: true,
encodingType: Camera.EncodingType.JPEG,
targetWidth: 100,
targetHeight: 100,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false,
correctOrientation: true
};
$cordovaCamera.getPicture(options).then(function (imageData) {
console.clear();
console.log(imageData);
$scope.imgURI = imageData;
$rootScope.ByteData = "data:image/jpeg;base64," + imageData;
}, function (err) {
var alertPopup = $ionicPopup.alert({
title: 'Invalid Image',
template: 'Please upload correct formated image!'
});
});
}
But whenever I run this code I got imageData in form of specific URL like "blob:http%3A//localhost%3A4400/f6c7fc38-a18f-484e-94d1-a52bb4e5fee2", How can I get base64 correct string in this scenario. In my view I am doing like
<ion-view view-title="{{application.CompanyName}}">
<ion-nav-buttons side="primary">
<button class="button" ng-click="saveApplication()">
SAVE
</button>
<button class="button" ng-click="goBack()">
BACK
</button>
</ion-nav-buttons>
<ion-content ng-controller="AppDetailCtrl">
<img ng-show="imgURI !== undefined" width="343px" height="300px" ng-src="{{imgURI}}">
<img ng-show="imgURI === undefined" width="343px" height="300px" ng-src="http://placehold.it/300x300">
<button class="button" ng-click="takePicture()">Upload Picture</button>
</ion-content>
</ion-view>
Actually I want to convert that blob image url type to base64 string and I was missing bellow method of converting like
function convertImgToBase64(url, callback, outputFormat) {
var canvas = document.createElement('CANVAS'),
ctx = canvas.getContext('2d'),
img = new Image;
img.crossOrigin = 'Anonymous';
img.onload = function () {
var dataURL;
canvas.height = img.height;
canvas.width = img.width;
ctx.drawImage(img, 0, 0);
dataURL = canvas.toDataURL(outputFormat);
callback.call(this, dataURL);
canvas = null;
};
img.src = url;
}
convertImgToBase64(imageData, function (base64Img) {
$rootScope.ByteData = base64Img;
});

$digest already in progress when i use ionicplatform.ready with camera functionality

This is my controller, i am trying to capture image with a button in page 1 and store it locally, and display image1 in page 1, then if i click the button again to take the pic of image2. Image2 should be displayed in page1 and image1 should be viewed in page2
.controller('LeadDetailController', [
'$scope',
'$cordovaDevice',
'$cordovaFile',
'$ionicPlatform',
'ImageService', 'FileService',
function( $scope,
$cordovaDevice,
$cordovaFile,
$ionicPlatform,
ImageService, FileService) {
// image capture code
$ionicPlatform.ready(function() {
console.log('ionic is ready');
$scope.images = FileService.images();
$scope.$apply();
});
$scope.urlForImage = function(imageName) {
var trueOrigin = cordova.file.dataDirectory + imageName;
return trueOrigin;
}
$scope.addImage = function(type) {
ImageService.handleMediaDialog(type).then(function() {
$scope.$apply();
});
}
at the initial stage itself i am getting this error
Error: [$rootScope:inprog] $digest already in progress
page1 with buttons
// here camera function is called to open the camera and take pic
<ion-option-button ng-click="addImage()"class="icon ion-android-camera"></ion-option-button>
//here the pic taken in camera should be displayed
<ion-option-button>
<img src="">
</ion-option-button>
//here moveing to the next page2
<ion-option-button ng-click="Page2()" class="icon ion-ios-grid-view"></ion-option-button>
page2 html
<ion-view>
<ion-nav-bar class="bar-positive">
<ion-nav-title class="title">Grid View</ion-nav-title>
</ion-nav-bar>
<ion-content class="has-header">
<img ng-repeat="image in images" ng-src="{{image.src}}" ng-click="showImages($index)" style="height:50%; width:50%; padding:2px ">
</ion-content>
</ion-view>
page2 controller
.controller('gridController', function($scope, $ionicBackdrop, $ionicModal, $ionicSlideBoxDelegate, $ionicScrollDelegate) {
//here the images are stored inside the array
$scope.images = [{ }];
services
.factory('FileService', function() {
var images;
var IMAGE_STORAGE_KEY = 'images';
function getImages() {
var img = window.localStorage.getItem(IMAGE_STORAGE_KEY);
if (img) {
images = JSON.parse(img);
} else {
images = [];
}
return images;
};
function addImage(img) {
images.push(img);
window.localStorage.setItem(IMAGE_STORAGE_KEY, JSON.stringify(images));
};
return {
storeImage: addImage,
images: getImages
}
})
.factory('ImageService', function($cordovaCamera, FileService, $q, $cordovaFile) {
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 optionsForType(type) {
var source;
/* switch (type) {
case 0:
source = Camera.PictureSourceType.CAMERA;
break;
case 1:
source = Camera.PictureSourceType.PHOTOLIBRARY;
break;
}*/
return {
destinationType: Camera.DestinationType.FILE_URI,
sourceType: source,
allowEdit: false,
encodingType: Camera.EncodingType.JPEG,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false
};
}
function saveMedia(type) {
return $q(function(resolve, reject) {
var options = optionsForType(type);
$cordovaCamera.getPicture(options).then(function(imageUrl) {
var name = imageUrl.substr(imageUrl.lastIndexOf('/') + 1);
var namePath = imageUrl.substr(0, imageUrl.lastIndexOf('/') + 1);
var newName = makeid() + name;
$cordovaFile.copyFile(namePath, name, cordova.file.dataDirectory, newName)
.then(function(info) {
FileService.storeImage(newName);
resolve();
}, function(e) {
reject();
});
});
})
}
return {
handleMediaDialog: saveMedia
}
});
could someone help me to fix this issue and to help me with page2 to imageviewing
As stated by #Ila the problem is likely due to $scope.$apply().
So if you can't predict if it has to be used then insert in an if statement as follows:
if(!$scope.$$phase) {
// no digest in progress...
$scope.$apply();
}
However this is not a good practice: prefer to use $scope.$apply() only when really needed (). See Angular docs
I agree with vb-platform. But what you can also do is wrapping your code into a $timeout so angularjs would handle the $apply for you ($timeout):
$ionicPlatform.ready(function() {
console.log('ionic is ready');
$timeout(function setImages() {
$scope.images = FileService.images();
});
});

Uploading files in Ionic application using Web API

My issue is as below.
I have given WEB API where I have to add boards picture.
What I have to DO?
User should able to select Image from Phone
User can add Name of board
When user click on submit, entered board name and board image should post using Web API with method of PUT. Here below is WEB API Details
WEB API Details
Header
URL: https://example.com
Content-Type: | Content Type |
Method: PUT
Data
board_id: 321
board_title: | Title |
board_background: | File |
I have used cordovaImagePicker plugin to select image and then I get stuck to uploading it to Server.
I can use cordova file transfer plugin but I think that will not help me in this case as there is no specified place to store image. All the file management done by WEB API, we have to just post file with Data.
After trying a lot solution I have come with below answer.
Board.html
<ion-view>
<ion-nav-bar class="bar">
<ion-nav-title>
<h1 class="title">
Create Board
</h1>
</ion-nav-title>
</ion-nav-bar>
<form name="boardForm" ng-submit="addBoard(data)">
<ion-content padding="false" scroll="true" has-bouncing="false">
<div id="form">
<div style="text-align: center; padding-top: 2%; padding-bottom: 2%;">
<div id="image-preview">
<label for="image-upload" id="image-label"><img src="{{ImagePrefix}}/task_plus.png" alt=""/></label>
<input type="file" name="board_background" id="image-upload" file-model="data.board_background">
</div>
<p>Add Cover</p>
</div>
<ion-list>
<ion-item style="background-color: #F8F8F8;">
<label class="control-label" for="board_name">BOARD NAME</label>
</ion-item>
<ion-item ng-class="{true:'error'}[submitted && boardForm.board_title.$invalid]">
<input type="text" id="board_name" ng-model="data.board_title"
placeholder="Add a Name" name="board_title" required>
<p ng-show="submitted && boardForm.board_title.$error.required">
Please enter a board name
</p>
</ion-item>
</ion-list>
</div>
</ion-content>
<ion-footer-bar>
<button class="button button-block control-button bottomOfPage"
ng-click="submitted = true">
CREATE
</button>
</ion-footer-bar>
</form>
</ion-view>
directive
.directive('fileModel', ['$parse', function ($parse) {
return {
restrict: 'A',
link: function (scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function () {
scope.$apply(function () {
modelSetter(scope, element[0].files[0]);
});
});
}
};
}])
Controller
.controller('ManageBoardCtrl', function ($scope, $http, $ionicPopup, $state, BoardService) {
$scope.submitted = false;
$scope.data = {};
$scope.addBoard = function(formData) {
BoardService.CreateBoard(formData).then(function(response) {
if (response === "success") {
$ionicPopup.alert({
title: "Success",
template: "Board created successfully"
});
}
}, function (response) {
$ionicPopup.alert({
title: "Failed",
template: "Somethings wrong, Can not create boards at now."
});
});
}
})
Service
.service('BoardService', function ($q, $http) {
var getUrl = API_URL + "boards";
var createBoard = function (fData) {
var formData = new FormData();
formData.append("board_title", fData.board_title);
formData.append("board_background", fData.board_background);
return $q(function (resolve, reject) {
$http({
transformRequest: angular.identity,
method: 'POST',
url: getUrl,
headers: { 'Content-Type': undefined },
data: formData
}).success(function (response) {
if (response.success === true) {
resolve('success');
} else {
reject('fail');
}
}).error(function () {
reject('requesterror');
});
});
};
return {
CreateBoard: createBoard
};
})
After deploying application for android / iPhone file selection will handle the browsing Images based on the OS.
One simple thing I can suggest,
Use input["file"] tag to select the image. You will get the file object and a temporary url. with this url you can show the image in the form.
Then use formData to append the image and the other field.
e.g.
var fd = new FormData();
fd.append('board_background', $scope.image, $scope.image.name);
fd.append('board_id', 321);
fd.append('board_title', 'Dummy title');
var xhr = new XMLHttpRequest();
xhr.open('PUT', YOUR_URL, true);
xhr.onload(function(res){
// Write your callback here.
});
// Send the Data.
xhr.send(fd);
Hope it will help you and meet your requirements.
First of all to need to select the image from device.
vm.getImages = function () {
var options = {
quality: 70,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
allowEdit: true,
correctOrientation:true,
encodingType: Camera.EncodingType.JPEG,
targetWidth: 300,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: true
};
navigator.camera.getPicture(onSuccess, onFail, options);
function onSuccess(imageURI) {
vm.image = "data:image/jpeg;base64," + imageURI;
vm.imageURI = imageURI;
}
function onFail(message) {
console.log('Failed because: ' + message);
}
};
You can change the source type for input if needed.
sourceType: Camera.PictureSourceType.CAMERA,
On success you get ImageURI either use it directly or convert it to base64 as mentioned below for uploading.
vm.image = "data:image/jpeg;base64," + imageURI;
After this you can use the FileTransfer plugin to upload file and track the progress at the same time.
cordovaFileTransfer.upload()
.then(function (result) {},
function (err) {},
function (progress) {});
Below link will definitely help you:
http://ionicmobile.blogspot.in/2015/10/jquery-file-upload.html
Make the appropriate changes if needed. Any help let me know...

Ionic-Angular.js Taking pictures & sending to server: null images

So I have managed to use a custom directive to upload images to my server, through Angular.js.
I have also managed to implement the camera functionality from Cordova.
Now I am trying to connect the two, but when sending images to the server, they get stored as null. I believe the problem lies in the fact that I was using an input field to get the image, and it was getting the full Object, and now I am getting just the image path after I take the picture and send it. My problem is, I don't really understand how I could convert the path to an Object, IF that is the problem?
index.html
<form class="form-horizontal" role="form">
<button class="picButton" ng-click="getPhoto()" class="button button-block button-primary">Take Photo</button>
<img ng-src="{{lastPhoto}}" style="max-width: 100%">
...
</form>
controllers.js
$scope.getPhoto = function() {
Camera.getPicture().then(function(imageURI) {
console.log(imageURI);
$scope.lastPhoto = imageURI;
$scope.upload(); <-- call to upload the pic
},
function(err) {
console.err(err);
}, {
quality: 75,
targetWidth: 320,
targetHeight: 320,
saveToPhotoAlbum: false
});
};
$scope.upload = function() {
var url = '';
var fd = new FormData();
//previously I had this
//angular.forEach($scope.files, function(file){
//fd.append('image',file)
//});
fd.append('image', $scope.lastPhoto);
$http.post(url, fd, {
transformRequest:angular.identity,
headers:{'Content-Type':undefined
}
})
.success(function(data, status, headers){
$scope.imageURL = data.resource_uri; //set it to the response we get
})
.error(function(data, status, headers){
})
}
When printing $scope.lastPhoto I get the image path: file:///var/mobile/Applications/../tmp/filename.jpg
EDIT
Request Headers:
------WebKitFormBoundaryEzXidt71U741Mc45
Content-Disposition: form-data; name="image"
file:///var/mobile/Applications/C65C8030-33BF-4BBB-B2BB-7ECEC86E87A7/tmp/cdv_photo_015.jpg
------WebKitFormBoundaryEzXidt71U741Mc45--
Should this causing the problem? Since I can see I am sending the path but not the image itself..
Before changing, I was using a custom directive, and was using $scope.files to append to my request in the upload function:
<input type="file" file-input="files" multiple onchange="angular.element(this).scope().filesChanged(this);upload();">
<button ng-click="upload()">Upload</button>
I resolve with this code :
$scope.getPhoto = function() {
navigator.camera.getPicture(onSuccess, onFail, { quality: 75, targetWidth: 320,
targetHeight: 320, destinationType: 0 });
//destination type was a base64 encoding
function onSuccess(imageData) {
//preview image on img tag
$('#image-preview').attr('src', "data:image/jpeg;base64,"+imageData);
//setting scope.lastPhoto
$scope.lastPhoto = dataURItoBlob("data:image/jpeg;base64,"+imageData);
}
function onFail(message) {
alert('Failed because: ' + message);
}
}
function dataURItoBlob(dataURI) {
// convert base64/URLEncoded data component to raw binary data held in a string
var byteString = atob(dataURI.split(',')[1]);
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
var ab = new ArrayBuffer(byteString.length);
var ia = new Uint8Array(ab);
for (var i = 0; i < byteString.length; i++)
{
ia[i] = byteString.charCodeAt(i);
}
var bb = new Blob([ab], { "type": mimeString });
return bb;
}
after this use append to your formdata like this:
formData.append('photo', $scope.lastPhoto, $scope.publish.name+'.jpg')
This code run on IOS without problem.
I think your problem lies in the 'Content-Type':undefined. For images, you should use 'Content-Type':'image/jpeg'.
I would also add enctype="multipart/form-data" to the <form>.
What I ended up doing was to add destinationType: Camera.DestinationType.DATA_URL in the options of the getPhoto function, and that returns a base64 representation of the image, which I then send over to the server and store in a TextField() (Django):
$scope.getPhoto = function() {
Camera.getPicture().then(function(imageURI) {
console.log(imageURI);
$scope.lastPhoto = imageURI;
$scope.upload(); <-- call to upload the pic
},
function(err) {
console.err(err);
}, {
quality: 75,
targetWidth: 320,
targetHeight: 320,
saveToPhotoAlbum: false,
destinationType: Camera.DestinationType.DATA_URL
});
};
Also, when deadling with Base64 images, remember to call the using:
<img ng-src="data:image/jpeg;base64,{{image}}" />

Resources