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

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

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

Processing multiple simultaneous uploads with Cordova

I want to upload 4 images from an ionic app to server side made using sails js.
An user can upload many images so before submit a form so I save all images in an array as its shown below
var cameraOptions = {
quality: 100,
destinationType: Camera.DestinationType.NATIVE_URI,
sourceType : Camera.PictureSourceType.CAMERA,
encodingType: Camera.EncodingType.JPEG,
targetWidth: 500,
targetHeight: 500,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false,
allowEdit:true
};
var success = function(data){
$mdDialog.hide();
if(key==null)
{
compteurImage =compteurImage+1;
$scope.$apply(function () {
$scope.imgURI.push(data);
});
$scope.nombreImage=compteurImage;
}
else
{
$scope.$apply(function () {
$scope.imgURI[key]=data;
});
}
$rootScope.image=$scope.imgURI;
};
After having had all the images in an array, I loop on the array and I send each image to the server as shown below
for (var i = 0; i<$rootScope.image.length; i++) {
if (keepGoing) {
var options = new FileUploadOptions();
var params = {};
params.idArticle =response.article.idArticle;
var url=$rootScope.image[i].substr($rootScope.image[i].lastIndexOf('/') + 1);
options = {
fileKey: "file",
fileName:i+(url.split('?')[0]),
mimeType: "image/png",
idArticle: response.article.idArticle
};
options.params=params;
var failed = function (err) {
console.log(err);
keepGoing = false;
};
var success = function (result) {
count++;
if(count==$rootScope.image.length)
{
console.log("success");
}
};
var ft = new FileTransfer();
ft.upload($rootScope.image[i], Globals.urlServer + Globals.port + "/article/uploadImage", success, failed, options);
}
Server side the controller in charge of upload image is /article/uploadImage
uploadImage:function(req,res)
{
req.file('file')
.upload({ dirname: '../../assets/imagesArticle'},function (err, uploadedFiles) {
if (err) return res.serverError(err);
else {
var chemin = '';
var type = '';
uploadedFiles.forEach(function (file) {
chemin = require('path').basename(file.fd);
type = file.type;
Image.create({cheminImage:chemin, typeImage:type,article:req.body.idArticle}).exec(function(err,image){
if (err)
{
res.send({success:false});
}
if(image)
{
res.send({success:true});
}
})
});
}
});
},
My issue is if regardless how many pictures I upload, when I look image directory on server side, images are always identical and correspond to the last image of the images array. For example if I upload 3 differents images, on server side I get 3 identicals images whose correspond to the third or last image in array.
how can I fix it ?

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...

how to convert image into base64 string?

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

Take in-app photo with Cordova Camera and upload it to Parse.com

I'm building an Ionic/Cordova app which uses Parse.com as a BaaS. It uses the ngCordova Camera plugin to control the device camera. The use-case is click a button, take a picture and have it upload to Parse. I've been researching the problem for a week now and still can't figure out why I can't get it to work.
The controller:
.controller('cameraCtrl', function($scope, camera) {
var cameraOptions = {
quality: 75,
destinationType: 0,
encodingType: 0,
targetWidth: 300,
targetHeight: 300,
mediaType: 0,
correctOrientation: true,
saveToPhotoAlbum: true
};
};
$scope.takePicture = function() {
cameraOptions.sourceType = 1;
navigator.camera.getPicture(onSuccess, onFail, cameraOptions);
}
$scope.selectPicture = function() {
cameraOptions.sourceType = 0;
navigator.camera.getPicture(onSuccess, onFail, cameraOptions);
}
function onSuccess(picture) {
File.upload(picture)
.success(function(data) {
// upload finish
});
$scope.$apply(function() {
$scope.preview = 'data:image/jpeg;base64,' + picture;
});
}
function onFail(resp) {
alert('Error: ' + resp);
}
});
The service:
angular.factory('File', function ($http) {
return {
upload: function (photo) {
var json = {
'base64': photo,
'_ContentType': 'image/jpeg'
}
var config = {
method: 'POST',
url: 'https://api.parse.com/1/files/pict.jpg',
data: json,
headers: {
'X-Parse-Application-Id': 'PCm0kDVeThvRcdFuS9lITrmskEhqjbqwFAydL2Lr',
'X-Parse-REST-API-Key': 'FhasGkTl0BLpJuLLJvPB2NFwlccXzVbirktdngXN'
}
};
return $http(config);
}
}
});
The HTML:
<button class="button" ng-click="takePicture()">
Any ideas as to why this doesn't work? Is there a better or more simple way to accomplish this? Any examples of this working somewhere? I've tried a dozen different solutions over the week and haven't found anything that works for my use-case. Thanks!
It would be helpful is you provided any error messages, but here is how I have solved the issue
var imageFile = new Parse.File("mypic.jpg", {base64: _params.photo});
console.log(imageFile);
// save the parse file
return imageFile.save().then(function () {
// create object to hold caption and file reference
var imageObject = new ImageObject();
// set object properties
imageObject.set("caption", _params.caption);
imageObject.set("picture", imageFile);
// save object to parse backend
return imageObject.save();
}, function (error) {
console.log("Error");
console.log(error);
});
There is a complete project here showing Parse.com integration with the File Object.
https://github.com/aaronksaunders/dcww/blob/master/www/js/services.js

Resources