Write file to sd card in android as image file - javascript

I would like to ask you here about some problem.
I am using Phonegap to build an application that can take photo and then show the picture in a canvas.After drawimage in the canvas, I use a method to convert canvas to image file. But I have a problem related to writing file as image file to SD Card in Android,i.e, I cannot read the image file that was created in SD Card (image is invalid).
Here is my code:
var picture = "";
function takePhoto() {
navigator.camera.getPicture(onCameraSuccess,
onCameraError,{
quality : 50,
destinationType : Camera.DestinationType.FILE_URI
//saveToPhotoAlbum: true
});
}
function onCameraSuccess(imageURL) {
var canvas = document.getElementById('myCanvas');
var ctx=canvas.getContext("2d");
var imageObj = new Image();
imageObj.onload = function() {
ctx.drawImage(imageObj, 0, 0,220,180);
};
imageObj.src=imageURL;
picture = imageURL;
}
function onCameraError(e) {
console.log(e);
navigator.notification.alert("onCameraError: " + e +" (" + e.code + ")");
}
function storePhoto() {
movePic(pictures);
}
function movePic(){
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fail);
}
function gotFS(fileSystem) {
fileSystem.root.getFile("test.PNG", {create: true, exclusive: false}, gotFileEntry, fail);
}
function gotFileEntry(fileEntry) {
fileEntry.createWriter(gotFileWriter, fail);
}
function gotFileWriter(writer) {
var c = document.getElementById('myCanvas');
var img_from_canvas=c.toDataURL("image/png"); // base64 encoded
var pic = img_from_canvas.split("base64,");
var pictures =window.atob(pic[1]); // decode base64
writer.write(pictures);
alert("Your picture was successfully stored !")
}
function fail(error) {
console.log(error.code);
}
I am appreciated for your helps and suggestion.

are you doing any kind of editing on image after capturing by canvas ?
if not then you can try this code to move file to root folder
var fs;
function movePic(){
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, gotFS, fail);
}
function gotFS(fileSystem) {
fs = fileSystem;
window.resolveLocalFileSystemURI(picture, gotFileEntry, fail);
}
function gotFileEntry(fileEntry) {
fileEntry.moveTo( fs.root,fileEntry.name, success, fail);
}
function fail(error) {
console.log(error.code);
}

You have problem with writer.write(pictures);
Try this instead
var buffer = new ArrayBuffer(pictures.length);
var array = new Uint8Array(buffer);
for (var i = 0; i < pictures.length; i++) {
array[i] = pictures.charCodeAt(i);
}
writer.write(buffer);
It work for me.

Related

Sending base64 image to Firebase Storage phonegap

I am having problem in sending my base64 image from phonegap (ios) to firebase storage. The main problem is firebase storage only accepted BLOB or File as attachment.
Heres my code for the camera function. Cordova-plugin-camera
function GetCamera(){
navigator.camera.getPicture( cameraSuccess, cameraError, {quality :50,
destinationType: Camera.DestinationType.DATA_URL,
encodingType: Camera.EncodingType.JPEG,
saveToPhotoAlbum: true});}
function to convert base 64 to blob
function b64toblob(b64_data, content_type) {
content_type = content_type || '';
var slice_size = 512;
var byte_characters = atob(b64_data);
var byte_arrays = [];
for(var offset = 0; offset < byte_characters.length; offset += slice_size) {
var slice = byte_characters.slice(offset, offset + slice_size);
var byte_numbers = new Array(slice.length);
for(var i = 0; i < slice.length; i++) {
byte_numbers[i] = slice.charCodeAt(i);
}
var byte_array = new Uint8Array(byte_numbers);
byte_arrays.push(byte_array);
}
var blob = new Blob(byte_arrays, {type: content_type});
return blob;};
Camera success function. take note that imageblob is a global variable
function cameraSuccess(imageData){
document.getElementById('Attachment1').innerHTML = "Attachment: True";
var image = imageData;
imageblob = b64toblob(image,"image/jpeg");}
putting the blob to firebase storage
try{
var storageRef = storage.ref().child('fire');
var uploadTask = storageRef.put(imageblob);
uploadTask.on('state_changed',null, null, function(){
var downloadURL = uploadTask.snapshot.downloadURL;
console.log("downloadURL :"+downloadURL);
});
i have tried every single thing, but its not working. Really need your guys help.. i am out of ideas
Cordova camera plugin doesn't return file object. That is problem with plugin.
But it returns all details about image. By using that you can create a blob or file object.
Reference for creating blob from file url.
var getFileBlob = function (url, cb) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.responseType = "blob";
xhr.addEventListener('load', function() {
cb(xhr.response);
});
xhr.send();
};
var blobToFile = function (blob, name) {
blob.lastModifiedDate = new Date();
blob.name = name;
return blob;
};
var getFileObject = function(filePathOrUrl, cb) {
getFileBlob(filePathOrUrl, function (blob) {
cb(blobToFile(blob, 'test.jpg')); // Second argument is name of the image
});
};
Calling function for get file blob
getFileObject('img/test.jpg', function (fileObject) { // First argument is path of the file
console.log(fileObject);
});
In your camera success function try this.
function cameraSuccess(imageData){
document.getElementById('Attachment1').innerHTML = "Attachment: True";
getFileObject(imageData.nativeURL, function(fileObject) {
console.log(fileObject);
var imgName = fileObject.name;
var metadata = { contentType: fileObject.type };
var uploadFile = storageRef.child("images/" + imgName).put(fileObject, metadata);
uploadFile.on(firebase.storage.TaskEvent.STATE_CHANGED, function(snapshot) {
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log(progress);
}, function(error) {
console.log(error);
}, function() {
var imgFirebaseURL = uploadFile.snapshot.downloadURL;
console.log(imgFirebaseURL);
});
});
}

Take a Picture and Save it in a Specific Folder PhoneGap

I'm developing an Android application with PhoneGap, my problem is I want to take a picture and save in a specified folder, I read a lot of tutorial online but I cannot find the solution on my problem, now I take the picture and save it in the default folder.
here is my js file.
var pictureSource;
var destinationType;
document.addEventListener("deviceready",onDeviceReady,false);
function onDeviceReady() {
pictureSource=navigator.camera.PictureSourceType;
destinationType=navigator.camera.DestinationType;
}
function onPhotoDataSuccess(imageURI) {
var smallImage = document.getElementById('smallImage');
smallImage.style.display = 'block';
smallImage.src = imageURI;
movePic(imageURI);
}
function capturePhoto() {
navigator.camera.getPicture(onPhotoDataSuccess, onFail, { quality: 50,
destinationType: destinationType.FILE_URI,
saveToPhotoAlbum: true});
}
function onFail(message) {
alert('Failed because: ' + message);
}
function movePic(file){
window.resolveLocalFileSystemURI(file, resolveOnSuccess, resOnError);
}
function resolveOnSuccess(entry){
var d = new Date();
var n = d.getTime();
var newFileName = n + ".jpg";
var myFolderApp = "Geofolder";
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function(fileSys) {
//The folder is created if doesn't exist
var direct = fileSys.root;
direct.getDirectory( myFolderApp,
{create:true, exclusive: false},
function(myFolderApp) {
entry.moveTo(myFolderApp, newFileName, successMove, resOnError);
},
resOnError);
},
resOnError);
}
function successMove(entry) {
sessionStorage.setItem('imagepath', entry.fullPath);
}
function resOnError(error) {
alert(error.code);
You have to get the directory entry.Try the following function.
function moveFile(fileUri) {
window.resolveLocalFileSystemURL(
fileUri,
function(fileEntry){
window.resolveLocalFileSystemURL(cordova.file.externalRootDirectory,
function(dirEntry) {
fileEntry.moveTo(dirEntry, "fileName.jpg", function(entry){
alert("File moved.check internal memory");
},resOnError);
},
resOnError);
},
resOnError);}

How to use AngularJS promises with Cordova Camera correctly?

I am having a series of functions that need to be executed in a synchronous order. One of these function is to get a picture using the Cordova Camera library from the phone of the user. Another function takes the image source and draws it in the DOM.
I work with Angular promises and the $q library to handle the synchronous execution. However, it seems that it somehow results in not updating the DOM when the functions are executed in order, even though the image was captured successfully and I have the url (ImageData).
To test, I added a function that draws the image on the DOM using the url that has been saved when the image has been captured from the phone. This does work. So there must be a problem with the use of my Angular Promises and handling of it.
How can I resolve this?
controllers ($q injected)
var sGetImage = function() {
var imageSrcOutput = "";
$scope.imageDataSrc = null;
try {
imageSrcOutput = CloakService.getImageData()
.then(function(imageData) {
//$scope.imageData = imageData;
$scope.imageDataSrc = "data:image/jpeg;base64," + imageData;
//$scope.imgURI = $scope.imageDataSrc;
return "data:image/jpeg;base64," + imageData;
});
// catch any errors with a struddle image
} catch (error) {
window.alert("No Image Picked. Drawing Struddle");
imageSrcOutput = "img/struddle.jpg";
$scope.imageDataSrc = imageSrcOutput;
}
return imageSrcOutput;
}
var sDrawImage = function(imageSrc) {
var deferred = $q.defer();
deferred.resolve(imageSrc)
window.alert("drawing image now with: " + imageSrc)
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.src = imageSrc;
img.onload = ctx.drawImage(img, 0, 0);
return deferred.promise;
}
// execution cycle (in controller)
sInit() // works fine
.then( sGetImage ) // image loaded in imageSrcOutput and $scope.imageDataSrc succesfully
.then( sDrawImage ) // here is the problem, it does not draw the images (tried both imageSrcOutput and $scope.imageDataSrc)
.then( sAddBanner )
.catch(function(error) {
window.alert("Catch Error: " + error)
})
services ($cordovaCamera injected)
var getImageData = function() {
var deferred = $q.defer();
var options = getOptions('CAMERA');
var imageDataOutput = $cordovaCamera.getPicture(options).then(function(imageData) {
// window.alert(imageData)
deferred.resolve(imageData)
return imageData; //todo:check
}, function(error) {
deferred.reject(error)
window.alert(error)
});
function getOptions(source) {
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: true
};
return options;
}
return deferred.promise;
//return imageDataOutput;
}
app.js
'use strict';
angular.module('myApp', [
'ionic',
'myApp.config',
'myApp.controllers',
'myApp.decorators',
'myApp.directives',
'myApp.filters',
'myApp.routes',
'myApp.services',
'angular-loading-bar',
'ngCordova' // the library to handle the picture
])
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
if(window.cordova && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
if(window.StatusBar) {
StatusBar.styleDefault();
}
});
})
Is this the problem:
img.onload=ctx.drawImage(img, 0, 0);
An onload handler needs to be a function. You are executing ctx.drawImage() immediately.
Try this:
img.onload = function () { ctx.drawImage(img, 0, 0); };
FYI, the stuff you are doing with $q.defer() in sDrawImage (and elsewhere) is unnecessary. If you want to pass imageSrc on to the next step, you can just do this:
var sDrawImage = function(imageSrc) {
window.alert("drawing image now with: " + imageSrc)
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var img = new Image();
img.src = imageSrc;
return $q(function (resolve) {
img.onload = function () {
ctx.drawImage(img, 0, 0);
resolve(imageSrc);
};
});
};
Any time you think of using $q.defer(), you should ask yourself "do I really need this?". 99.5% of the time, the answer should be "No."

Merging multiple parts of a file in Cordova

Within my Cordova app, I am downloading arbitrary files like images or video files. This is done with the Cordova file-transfer plugin and the "Range" Header, because I need to download the files in parts.
My Problem is, that I want to merge back the several small "Byte"-Files back together into the original file they once where to use that file. Every time I'm trying to read the resulting parts as binaryString via the FileReader and write them together in a new file, that file ends up a lot larger than the parts of the original file altogther and the resulting file is unusable.
Any help is appreciated.
Here is my code until now (long and ugly):
document.addEventListener('deviceready', deviceready, false);
var App;
var finishedFileUrl = "";
var async = {
sequence: function(items, callback) {
var def = $.Deferred(),
deferrers = [$.Deferred()];
for(var i = 0; i < items.length; i++) {
(function (n) {
deferrers[n + 1] = $.Deferred();
deferrers[n].always(function() {
callback(items[n], deferrers[n + 1]);
});
})(i);
}
deferrers[items.length].always(function() {
def.resolve();
});
deferrers[0].resolve();
return def.promise();
}
}
var aSmallImageArray = [
'' // Put URL to JPG accessible with Range Header Request here
];
var aByteSizeImageArray = [];
function formatDownloadArray(fileSize) {
for(var j = 1000; j <= fileSize; j += 1000) {
aByteSizeImageArray.push(j);
}
aByteSizeImageArray.push(j);
}
function deviceready() {
console.log('dv ready');
function registerHandlers() {
App = new DownloadApp();
formatDownloadArray(XXXXX); // XXXXX should be size of JPG in bytes
document.getElementById("startDl").onclick = function() {
var that = this;
console.log("load button clicked");
var folderName = "testimagefolder";
// sequence call
async.sequence(aByteSizeImageArray, function(currentBytes, iter) {
var filePath = aSmallImageArray[0];
var fileName = aSmallImageArray[0].substr(52,99) + currentBytes;
console.log(filePath);
console.log(fileName);
console.log("Starting with: " + fileName);
var uri = encodeURI(filePath);
var folderName = "testimagefolder";
document.getElementById("statusPlace").innerHTML = "<br/>Loading: " + uri;
App.load(currentBytes, uri, folderName, fileName,
function progress (percentage) {
document.getElementById("statusPlace").innerHTML = "<br/>" + percentage + "%";
},
function success (entry) {
console.log("Entry: " + entry);
document.getElementById("statusPlace").innerHTML = "<br/>Image saved to: " + App.filedir;
console.log("DownloadApp.filedir: " + App.filedir);
iter.resolve();
},
function error () {
document.getElementById("statusPlace").innerHTML = "<br/>Failed load image: " + uri;
iter.resolve();
}
);
}).then(function afterAsync () {
console.log("ASYNC DONE");
var ohNoItFailed = function ohNoItFailed (exeperro) {
console.log(exeperro);
}
// now we merge the fileparts into one file to show it
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (FileSystem) {
FileSystem.root.getDirectory(folderName, {create: true, exclusive: false}, function itSuccessed (Directory) {
Directory.getFile(aSmallImageArray[0].substr(52,99), {create: true, exclusive: false}, function itSuccessedAgain (fileEntry) {
finishedFileUrl = fileEntry.toURL();
var directoryReader = Directory.createReader();
var allFiles = directoryReader.readEntries(function succesReadDir (fileEntries) {
async.sequence(fileEntries, function(currentFile, iterThis) {
currentFile.file(function (theActualFile) {
var myFileReader = new FileReader();
myFileReader.onload = function (content) {
console.log('FileReader onload event fired!');
console.log('File Content should be: ' + content.target.result);
fileEntry.createWriter(
function mergeImage (writer) {
writer.onwrite = function (evnt) {
console.log("Writing successful!");
iterThis.resolve();
}
writer.seek(writer.length);
writer.write(content.target.result);
}, ohNoItFailed);
};
myFileReader.readAsBinaryString(theActualFile);
}, ohNoItFailed);
}).then(function afterAsyncTwo () {
console.log("NOW THE IMAGE SHOULD BE TAKEN FROM THIS PATH: " + finishedFileUrl);
//window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, function (FileSystem) {
//FileSystem.root.getDirectory(folderName, {create: true, exclusive: false}, function itSuccessed (Directory) {
//Directory.getFile(aSmallImageArray[0].substr(52,99), {create: true, exclusive: false}, function itSuccessedAgain (fileEntry) {
//fileEntry.createWriter(
document.getElementById("image_here").src = finishedFileUrl;
});
}, ohNoItFailed);
}, ohNoItFailed);
}, ohNoItFailed);
}, ohNoItFailed);
});
};
}
registerHandlers();
}
var DownloadApp = function() {}
DownloadApp.prototype = {
filedir: "",
load: function(currentBytes, uri, folderName, fileName, progress, success, fail) {
var that = this;
that.progress = progress;
that.success = success;
that.fail = fail;
filePath = "";
that.getFilesystem(
function(fileSystem) {
console.log("GotFS");
that.getFolder(fileSystem, folderName, function(folder) {
filePath = folder.toURL() + fileName;
console.log("FILEPATH: " + filePath);
console.log("URI: " + uri);
that.transferFile(currentBytes, uri, filePath, progress, success, fail);
}, function(error) {
console.log("Failed to get folder: " + error.code);
typeof that.fail === 'function' && that.fail(error);
});
},
function(error) {
console.log("Failed to get filesystem: " + error.code);
typeof that.fail === 'function' && that.fail(error);
}
);
},
getFilesystem: function (success, fail) {
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, success, fail);
},
getFolder: function (fileSystem, folderName, success, fail) {
fileSystem.root.getDirectory(folderName, {create: true, exclusive: false}, success, fail)
},
transferFile: function (currentBytes, uri, filePath, progress, success, fail) {
var that = this;
that.progress = progress;
that.success = success;
that.fail = fail;
console.log("here we go");
console.log("filePath before Request: " + filePath);
var previousBytes = currentBytes - 1000;
var transfer = new FileTransfer();
transfer.onprogress = function(progressEvent) {
if (progressEvent.lengthComputable) {
var perc = Math.floor(progressEvent.loaded / progressEvent.total * 100);
typeof that.progress === 'function' && that.progress(perc); // progression on scale 0..100 (percentage) as number
} else {
}
};
transfer.download(
uri,
filePath,
function success (entry) {
console.log("File saved to: " + entry.toURL());
typeof that.success === 'function' && that.success(entry);
},
function errorProblem(error) {
console.log("An error has occurred: Code = " + error.code);
console.log("download error source " + error.source);
console.log("download error target " + error.target);
console.log("download error code " + error.code);
typeof that.fail === 'function' && that.fail(error);
},
true,
{
headers: {
"Range": "bytes=" + previousBytes + "-" + currentBytes
}
}
);
}
}
async code by stackoverflow-user: Paul Facklam
-> Thanks a lot!
you can build a blob from other blobs, like the ones you use FileReader on now. (File()s are Blobs)
// put three blobs into a fourth:
var b=new Blob([new Blob(["hello"]), new Blob([" "]), new Blob(["world"])]);
// verify the blob has the data we expect:
var fr=new FileReader();
fr.onload=function(){alert(this.result);};
fr.readAsBinaryString(b); // shows: "hello world"
the binaryString flavor is used here to show how these low-order strings stack up, but the actual new blob instance should have all the orig (arbitrary) bytes from the original blobs, even if they aren't composed of simple strings...
Using readAsArrayBuffer() instead of readAsBinaryString() did the trick!
So instead of:
myFileReader.readAsBinaryString(theActualFile);
I did:
myFileReader.readAsBinaryArray(theActualFile);
And the resulting image file is usable.

accessing cache pictures phonegap

I'm saving pictures in my application using the camera feature of Phonegap. When I try to get the file through its saved file_URI (that I got from the camera), the image doesn't load.
function toBase64(url) {
var canvas = document.createElement("canvas");
var ctx = canvas.getContext('2d');
var img = new Image();
img.src = url;
if ( img.height != 0 ) {
var height = img.height, width = img.width;
canvas.height = height;
canvas.width = width;
ctx.drawImage(img, 0, 0, width, height);
try {
var dataURL = canvas.toDataURL("image/jpg");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");
}
catch (err) { console.log("ERROR " + err);}
}
else {
alert("Wrong path!");
}
}
The images are saved in the cache folder of the application (/data/data/my.app/cache)
Any ideas of where the problem could be from ?
I fixed this issue and had to use the FileReader object of Phonegap.
var base64;
function toBase64(filename) {
window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, function(fileSystem) {
// Filesystem's root is the cache folder of the application: /storage/emulated/0/Android/data/com.yourcompany.whatever
fileSystem.root.getFile(filename, null, function(fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(evt) {
console.log(evt.target.result);
// Firing callback
base64 = evt.target.result;
};
reader.readAsDataURL(file);
}, fail);
}, fail);
}, fail);
// We wait until the reader was fully loaded and then we return the base64 encrypted data !
while ( base64 == "" ) {}
base64 = base64.replace(/^data:image\/(png|jpg|jpeg);base64,/, "");
return base64;
}
Don't think that the "while(base64 == "" ) {}" is a good practice though...
EDIT: I used the do_when method of this gentleman instead of the empty loop !

Categories

Resources