It is possible to upload base64 image to Firebase ?
I have tried this code :
var storageRef = firebase.storage().ref();
console.log(storageRef);
var file = "data:image/jpeg;base64,BASE64.....";
var uploadTask = storageRef.child('avatars/'+user.providerData[0].uid+'/photo-'+$scope.number+'.jpg').put(file);
uploadTask.on('state_changed', function(snapshot){
}, function(error) {
console.log('error');
}, function() {
console.log('success');
var downloadURL = uploadTask.snapshot.downloadURL;
});
But i have an error :
{code: "storage/invalid-argument", message: "Firebase Storage: Invalid argument in `put` at index 0: Expected Blob or File.", serverResponse: null, name: "FirebaseError"}
You only need to use the putString function without converting the BASE64 to blob.
firebase.storage().ref('/your/path/here').child('file_name')
.putString(your_base64_image, ‘base64’, {contentType:’image/jpg’});
Make sure to pass the metadata {contentType:’image/jpg’} as the third parameter (optional) to the function putString in order for you to retrieve the data in an image format.
or simply put:
uploadTask = firebase.storage().ref('/your/path/here').child('file_name').putString(image, 'base64', {contentType:'image/jpg'});
uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED, // or 'state_changed'
function(snapshot) {
// Get task progress, including the number of bytes uploaded and the total number of bytes to be uploaded
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log('Upload is ' + progress + '% done');
switch (snapshot.state) {
case firebase.storage.TaskState.PAUSED: // or 'paused'
console.log('Upload is paused');
break;
case firebase.storage.TaskState.RUNNING: // or 'running'
console.log('Upload is running');
break;
}
}, function(error) {
console.log(error);
}, function() {
// Upload completed successfully, now we can get the download URL
var downloadURL = uploadTask.snapshot.downloadURL;
});
You can then use the downloadURL to save to firebase.database() and/or to put as an src to an <img> tag.
You can pass the base64 into a function that returns a blob such as this:
base64toBlob(base64Data, contentType) {
contentType = contentType || '';
var sliceSize = 1024;
var byteCharacters = atob(base64Data);
var bytesLength = byteCharacters.length;
var slicesCount = Math.ceil(bytesLength / sliceSize);
var byteArrays = new Array(slicesCount);
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
var begin = sliceIndex * sliceSize;
var end = Math.min(begin + sliceSize, bytesLength);
var bytes = new Array(end - begin);
for (var offset = begin, i = 0 ; offset < end; ++i, ++offset) {
bytes[i] = byteCharacters[offset].charCodeAt(0);
}
byteArrays[sliceIndex] = new Uint8Array(bytes);
}
return new Blob(byteArrays, { type: contentType });
}
Firebase Storage takes the JS File or Blob types, rather than a string. You can store your base64 encoded data in a file and then upload it, though I recommend converting them to a "real" file (jpg or png judging on it looks like a photo) so you can have a content type, have browsers treat it as such, get benefits like compression, etc.
Related
While upgrading from Firebase 8 to 9 I've hit a problem. I need to monitor the upload progress of uploadString but uploadTask.on seems to fail.
var uploadTask = uploadString(ref(this.$storage, 'profile.jpg'), canvas.toDataURL('image/jpeg', 0.8), 'data_url');
uploadTask.on('state_changed',
(snapshot) => {
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log('Upload is ' + progress + '% done');
},
(error) => {
// Handle unsuccessful uploads
},
() => {
// Handle successful uploads on complete
}
);
The image gets uploaded but with the following error:
"TypeError: uploadTask.on is not a function"
uploadTask.on with putString in version 8 works fine. Anyone know what is going on? Thanks in advance.
I found a work around for anyone interested, it works specifically for canvas elements and uses uploadBytesResumable instead. Still interested in how do achieve this with uploadString if anyone knows.
var img = canvas.toDataURL('image/jpeg', 0.8);
var file = now.dataURItoBlob(img);
var uploadTask = uploadBytesResumable(ref(now.$storage, 'profile.jpg'), file);
uploadTask.on('state_changed',
(snapshot) => {
const progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
console.log('Upload is ' + progress + '% done');
},
(error) => {
// Handle unsuccessful uploads
},
() => {
// Handle successful uploads on complete
}
);
dataURItoBlob function is as follows
dataURItoBlob(dataURI) {
var byteString;
if (dataURI.split(',')[0].indexOf('base64') >= 0){
byteString = atob(dataURI.split(',')[1]);
}
else{
byteString = unescape(dataURI.split(',')[1]);
}
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var ia = new Uint8Array(byteString.length);
for (var i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ia], {type:mimeString});
}
uploadString() returns an uploadTask.You use it to monitor the status of your upload
I am working on a function that will write data to a remote server in chunks using a 3rd party API. Through some help on Stack Overflow I was able to accomplish this, where it is now working as expected. The problem is that I can only get a single 16kb chunk to write as I will need to advance the pos of where the next bytes are written to.
The initial write starts at 0 easily enough. Due to my unfamiliarity with this though, I am unsure if the next pos should just be 16 or what. If it helps, the API call writeFileChunk() takes 3 parameters, filepath (str), pos (int64), and data (base64 encoded string).
reader.onload = function(evt)
{
// Get SERVER_ID from URL
var server_id = getUrlParameter('id');
$("#upload_status").text('Uploading File...');
$("#upload_progress").progressbar('value', 0);
var chunkSize = 16<<10;
var buffer = evt.target.result;
var fileSize = buffer.byteLength;
var segments = Math.ceil(fileSize / chunkSize); // How many segments do we need to divide into for upload
var count = 0;
// start the file upload
(function upload()
{
var segSize = Math.min(chunkSize, fileSize - count * chunkSize);
if (segSize > 0)
{
$("#upload_progress").progressbar('value', (count / segments));
var chunk = new Uint8Array(buffer, count++ * chunkSize, segSize); // get a chunk
var chunkEncoded = btoa(String.fromCharCode.apply(null, chunk));
// Send Chunk data to server
$.ajax({
type: "POST",
url: "filemanagerHandler.php",
data: { 'action': 'writeFileChunk', 'server_id': server_id, 'filepath': filepath, 'pos': 0, 'chunk': chunkEncoded },
dataType: 'json',
success: function(data)
{
console.log(data);
setTimeout(upload, 100);
},
error: function(XMLHttpRequest, textStatus, errorThrown)
{
alert("Status: " + textStatus); alert("Error: " + errorThrown); alert("Message: " + XMLHttpRequest.responseText);
}
});
}
else
{
$("#upload_status").text('Finished!');
$("#upload_progress").progressbar('value', 100);
getDirectoryListing(curDirectory);
}
})()
};
The current position for the file on client side would be represented by this line, or more specifically the second argument at the pre-incremental step:
var chunk = new Uint8Array(buffer, count++ * chunkSize, segSize);
though, in this case it advances (count++) before you can reuse it so if you need the actual position (below as pos) you can extract it by simply rewriting the line into:
var pos = count++ * chunkSize; // here chunkSize = 16kb
var chunk = new Uint8Array(buffer, pos, segSize);
Here each position update will increment 16kb as that is the chunk-size. For progress then it is calculated pos / fileSize * 100. This of course assuming using the unencoded buffer size.
The only special case is the last chunk, but when there are no more chunks left to read the position should be equal to the file length (fileSize) so it should be pretty straight-forward.
When the ajax call return the server should have the same position unless something went wrong (connection, write access change, disk full etc.).
You can use Filereader API to read the chunks and send it to your remote server.
HTML
<input type="file" id="files" name="file" /> Read bytes:
<span class="readBytesButtons">
<button>Read entire file in chuncks</button>
</span>
Javascript
// Post data to your server.
function postChunk(obj) {
var url = "https://your.remote.server";
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open('post', url, true);
xhr.responseType = 'json';
xhr.onload = function() {
var status = xhr.status;
if (status == 200) {
resolve(xhr.response);
} else {
reject(status);
}
};
var params = "";
// check that obj has the proper keys and create the url parameters
if (obj.hasOwnProperty(action) && obj.hasOwnProperty(server_id) && obj.hasOwnProperty(filepath) && obj.hasOwnProperty(pos) && obj.hasOwnProperty(chunk)) {
params += "action="+obj[action]+"&server_id="+obj[server_id]+"&filepath="+obj[filepath]+"&pos="+obj[pos]+"&chunk="+obj[chunk];
}
if(params.length>0) {
xhr.send(params);
} else {
alert('Error');
}
});
}
// add chunk to "obj" object and post it to server
function addChunk(reader,obj,divID) {
reader.onloadend = function(evt) {
if (evt.target.readyState == FileReader.DONE) { // DONE == 2
obj.chunk = evt.target.result;
console.log(obj);
document.getElementById(divID).textContent +=
['Sending bytes: ', obj.pos*16000, ' - ', ((obj.pos*16000)+(obj.pos+1)*obj.chunk.length),
'\n'].join('');
// post data to server
postChunk(obj).then(function(data) {
if(data!=="" && data!==null && typeof data!=="undefined") {
// chunk was sent successfully
document.getElementById(divID).textContent +=
['Sent bytes: ', obj.pos*16000, ' - ', ((obj.pos*16000)+(obj.pos+1)*obj.chunk.length),'\n'].join('');
} else {
alert('Error! Empty response');
}
}, function(status) {
alert('Resolve Error');
});
}
};
}
// read and send Chunk
function readChunk() {
var files = document.getElementById('files').files;
if (!files.length) {
alert('Please select a file!');
return;
}
var file = files[0];
var size = parseInt(file.size);
var chunkSize = 16000;
var chunks = Math.ceil(size/chunkSize);
var start,stop = 0;
var blob = [];
for(i=0;i<chunks;i++) {
start = i*chunkSize;
stop = (i+1)*chunkSize-1;
if(i==(chunks-1)) {
stop = size;
}
var reader = new FileReader();
blob = file.slice(start, stop);
reader.readAsBinaryString(blob);
var obj = {action: 'writeFileChunk', server_id: 'sid', filepath: 'path', pos: i, chunk: ""};
var div = document.createElement('div');
div.id = "bytes"+i;
document.body.appendChild(div);
addChunk(reader,obj,div.id);
}
}
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
console.log(' Great success! All the File APIs are supported.');
} else {
alert('The File APIs are not fully supported in this browser.');
}
document.querySelector('.readBytesButtons').addEventListener('click', function(evt) {
if (evt.target.tagName.toLowerCase() == 'button') {
readChunk();
}
}, false);
You can check this example in this Fiddle
I used a jquery plugin to crop images. The plugin will crop the image and give it to me as a base64 encoded string. In order to upload it to S3, I need to convert this into a file and pass the file into the upload function. How can I do this? I tried a lot of things including decoding the string using atob. None worked.
Here's the code of the cropping plugin ('croppie') which gives me the encoded string :
imageCropper.croppie('result', {
type: 'canvas',
size: 'viewport',
format: 'jpeg'
}).then(function (resp) {
updateAvatar(resp);
});
I pass it to a function called updateAvatar. Here's the updateAvatar function :
updateAvatar({Meteor, Slingshot}, avatar) {
const uploader = new Slingshot.Upload('userAvatarUpload');
uploader.send(avatar, function (error, downloadUrl) {
if (error) {
// Log service detailed response.
console.error('Error uploading', uploader.xhr.response);
console.log(error);
}
else {
console.log('uploaded', downloadUrl);
}
});
}
The uploader.send function expects a file or a url. It won't accept my encoded string.
The plugin which I use to upload files to S3 : https://github.com/CulturalMe/meteor-slingshot
It seems like the missing 'brick' in your code is a function that would take a base64-encoded image and convert it to a Blob.
So, I'm going to focus on that part exclusively with a short comment for each step.
The following function expects a string such as:
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICA...
function base64ImageToBlob(str) {
// extract content type and base64 payload from original string
var pos = str.indexOf(';base64,');
var type = str.substring(5, pos);
var b64 = str.substr(pos + 8);
// decode base64
var imageContent = atob(b64);
// create an ArrayBuffer and a view (as unsigned 8-bit)
var buffer = new ArrayBuffer(imageContent.length);
var view = new Uint8Array(buffer);
// fill the view, using the decoded base64
for(var n = 0; n < imageContent.length; n++) {
view[n] = imageContent.charCodeAt(n);
}
// convert ArrayBuffer to Blob
var blob = new Blob([buffer], { type: type });
return blob;
}
Convert the base64 string to blob, to be used in upload to S3. There are tidier ways of doing this of course! :)
Original SO Answer here: https://stackoverflow.com/a/16245768/1350913
imageCropper.croppie('result', {
type: 'canvas',
size: 'viewport',
format: 'jpeg'
}).then(function(resp) {
var contentType = 'image/png';
var s3Blob = b64toBlob(resp, contentType);
updateAvatar(s3Blob);
});
updateAvatar({
Meteor,
Slingshot
}, avatar) {
const uploader = new Slingshot.Upload('userAvatarUpload');
uploader.send(avatar, function(error, downloadUrl) {
if (error) {
// Log service detailed response.
console.error('Error uploading', uploader.xhr.response);
console.log(error);
} else {
console.log('uploaded', downloadUrl);
}
});
}
function b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
var b64DataString = b64Data.substr(b64Data.indexOf(',') + 1);
var byteCharacters = atob(b64DataString);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
var blob = new Blob(byteArrays, {
type: contentType
});
return blob;
}
The base64ToFile function (.ts) converts the base64 string into a File. The codeUnits and charCodes part make sure you can read Unicode text as ASCII by converting the string such that each 16-bit unit occupies only one byte.
Finally the download function (.ts) downloads the converted file from your browser to your local machine.
function base64ToFile(base64data: string, myFileNameWithdotExtention: string,
fileType: string): File {
let content = decodeURIComponent(escape(window.atob(base64data)));
let fileName = myFileNameWithdotExtention;
const codeUnits = Uint16Array.from(
{ length: content.length },
( _, index) => content.charCodeAt(index)
);
const charCodes = new Uint8Array(codeUnits.buffer);
const type = fileType; // 'text/csv' for instance
const blob = new Blob([charCodes], { type });
return new File([blob], fileName, { lastModified: new Date().getTime(), type });
}
download(){
let res: string = getMyDataFromSomewhere(); // base64 string
let data = base64ToFile(res);
let element = document.createElement('a');
window.URL = window.URL || window.webkitURL;
element.setAttribute('href', window.URL.createObjectURL(data));
element.setAttribute('download', data.name);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
photos is an array filled by blobs
var metadata = {
contentType: 'image/jpeg',
};
for(let i = 0; i < photos.length; i++) {
let photoName = photos[i].file.name;
if(photos[i].resized) photos[i] = this.dataURLToBlob(photos[i].resized.dataURL);
var uploadTask = firebase.storage().ref().child('images/' + this.userInfo.uid + '/offers/' + new Date().getTime() + photoName).put(photos[i], metadata);
uploadTask.on('state_changed', function(snapshot){
}, function(error) {
}, function() {
console.log(uploadTask.snapshot.downloadURL);
.
.
.
I have a problem with asynchronous, because sometimes (every time actually) console.log prints few null.
For example I upload 3 photos.
I get a message:
null
2x third photo download url
What's going on?
The problem was in
var uploadTask =... which should be let uploadTask = ...
I'm trying to store a canvas in my divice, reading in internet i found that i have to convert my canvas to Base64, nice, i did it...
Then i searched a function to store a base64 with cordova and just found a function that stores a Blob object, so i searched again and found a function that converts my base64 to Blob, and nice again, it apparently works, but when i go to the file explorer i'm just getting a file that says in plain text (and changing its extension to .txt ):
[object Uint8Array][object Uint8Array]
This is my final code:
function draw() {
window.requestFileSystem(window.TEMPORARY, 5 * 1024 * 1024, function (fs) {
console.log('file system open: ' + fs.name);
getSampleFile(fs.root);
}, errorHandler);
}
function getSampleFile(dirEntry) {
var canvas = document.getElementById("mycanvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
//...some code to customize the canvas
//var mURI = canvas.toDataURL();
var mURI = canvas.toDataURL().replace(/data:image\/png;base64,/,'');
var x = Math.floor((Math.random() * 100000) + 1);
saveFile(dirEntry, b64toBlob(mURI,"image/png","512") , x+".png");
}
}
function b64toBlob(b64Data, contentType, sliceSize) {
contentType = contentType || '';
sliceSize = sliceSize || 512;
console.log('Estoy en B64');
var byteCharacters = atob(b64Data);
var byteArrays = [];
for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
var slice = byteCharacters.slice(offset, offset + sliceSize);
var byteNumbers = new Array(slice.length);
for (var i = 0; i < slice.length; i++) {
byteNumbers[i] = slice.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
byteArrays.push(byteArray);
}
console.log('ByteArrays'+byteArrays);
var blob = new Blob(byteArrays, {type: contentType});
return blob;
}
function saveFile(dirEntry, fileData, fileName) {
console.log('1. DIRENTRY:'+dirEntry+', 2 FILEDATA:'+fileData+',3 FILENAME:'+fileName);
dirEntry.getFile(fileName, { create: true, exclusive: false }, function (fileEntry) {
writeFile(fileEntry, fileData);
}, errorHandler);
}
function writeFile(fileEntry, dataObj, isAppend) {
fileEntry.createWriter(function (fileWriter) {
fileWriter.onwriteend = function() {
console.log("Successful file write...");
};
fileWriter.onerror = function(e) {
console.log("Failed file write: " + e.toString());
};
fileWriter.write(dataObj);
});
}
I hope you can help me, the main goal of this is to store a picture with a watermark, so if you have another idea please tell me, thanks
Since you need to add a water mark you may refer following links
W3School Link
Jsfiddle Link
EDITED
var yourCanvas = document.getElementById('yourCanvasId');
var context = yourCanvas.getContext('2d');
var waterMarkImg = document.getElementById("waterMarkImageId");
context.drawImage(waterMarkImg, 0, 0, yourCanvas.width, yourCanvas.height);
var basse64 = yourCanvas.toDataURL();