Monitoring upload using uploadString in Firebase v9 error - javascript

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

Related

Blockchain Tierion API - Real time File upload using Node.js

Subject: Real time file upload using Node.js
Hi all,
I am working on real file upload using Node.js and i am facing the error in below mentioned code:
function handleFile(files) {
if (!files.length) {
return;
}
var file = files[0];
var reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onprogress = function (e) {
dragZone.removeClass( 'is-uploading' );
dragZone.removeClass( 'is-error' );
dragZone.addClass( 'is-success' );
if (e.lengthComputable) {
var percentComplete = (e.loaded / e.total)*100;
percentComplete = parseInt(percentComplete);
//Do something with upload progress
console.log(percentComplete);
$('#status').text(percentComplete+'%');
$('.progress-bar').text(percentComplete+'%');
$('#progressBar').val(percentComplete);
//console.log(e.loaded+ " / " + e.total)
}
}
reader.onload = function(e) {
var data = e.target.result;
//Error-facing
window.crypto.subtle.digest({name: 'SHA-256'}, data).then(function(hash) {
var hexString = '';
var bytes = new Uint8Array(hash);
for (var i = 0; i < bytes.length; i++) {
var hex_i = bytes[i].toString(16);
hexString += hex_i.length === 1 ? '0' + hex_i : hex_i;
}
$('#hash').text(hexString);
calculatedHash = hexString;
dragZone.removeClass( 'is-uploading' );
dragZone.removeClass( 'is-error' );
dragZone.addClass( 'is-success' );
}).catch(function(e) {
showError(e);
});
};
Anybody please guide me how to resolve this error:
index.js:138 Uncaught TypeError: Cannot read property 'digest' of undefined
at FileReader.reader.onload
There is no data passing to this hash variable below mentioned line:
window.crypto.subtle.digest({name: 'SHA-256'}, data).then(function(hash)
I presume you use this on a site with http? the crypto api is not available on insecure domains, you need to use https for digest to work

Multiple progress bars for multiple files upload firebase

I am working with Firebase Storage and trying to upload multiple files at a time. What I have done so far is, i get the files, and upload one by one to firebase and only one progress bar is there which shows the progress for each uploading file. What I need is, when i select the files, i want to create the progress bars equal to the number of files and they will start uploading and each progress bar will be shown its own progress.
What I have done so far is :
var up = document.getElementById("fileUpload"),
pr = document.getElementsByClassName("progress")[0];
list = document.getElementsByClassName("list")[0];
//Listen for file selection
up.addEventListener('change', function(e){
//Get files
for (var i = 0; i < e.target.files.length; i++) {
var imageFile = e.target.files[i];
uploadImageAsPromise(imageFile,i);
}
});
//Handle waiting to upload each file using promise
function uploadImageAsPromise (imageFile,i) {
return new Promise(function (resolve, reject) {
var storageRef = firebase.storage().ref($.cookie("_lo")+"/"+imageFile.name);
//Upload file
var task = storageRef.put(imageFile);
//Update progress bar
task.on('state_changed',
function progress(snapshot){
var percentage = snapshot.bytesTransferred / snapshot.totalBytes * 100;
pr.value = percentage;
},
function error(err){
},
function complete(){
var downloadURL = task.snapshot.downloadURL;
console.log("file " + (i+1) + " Uplaoded");
console.log(downloadURL);
}
);
});
}
This is the quickest Solution I came with :
var up = document.getElementById("fileUpload"),
pr = document.getElementsByClassName("progress");
list = document.getElementsByClassName("list")[0];
//Listen for file selection
up.addEventListener('change', function(e){
//Get files
for (var i = 0; i < e.target.files.length; i++) {
var imageFile = e.target.files[i];
list.insertAdjacentHTML('afterbegin','<li class="row">'+
'<div class="col-4">'+imageFile.name+'</div>'+
'<div class="col-4">'+imageFile.size+'</div>'+
'<progress value="0" max="100" class="progress"></progress>'+
'</li>');
uploadImageAsPromise(imageFile,i);
}
});
//Handle waiting to upload each file using promise
function uploadImageAsPromise (imageFile,i) {
return new Promise(function (resolve, reject) {
var storageRef = firebase.storage().ref($.cookie("_lo")+"/"+imageFile.name);
//Upload file
var task = storageRef.put(imageFile);
//Update progress bar
task.on('state_changed',
function progress(snapshot){
var percentage = snapshot.bytesTransferred / snapshot.totalBytes * 100;
pr[i].value = percentage;
},
function error(err){
},
function complete(){
var downloadURL = task.snapshot.downloadURL;
console.log("file " + (i+1) + " Uplaoded");
console.log(downloadURL);
}
);
});
}

Vue 2 + Firebase - Execute function after upload to Firebase Storage

I would like to execute the code this.createListing() after the for-loop has finished and the upload is complete.
If I run it after the for-loop, it doesn't take into consideration if all the uploads have finished or not. Thus it will not get the downloadURL from the uploaded files.
Ideally I would like to run the function after all uploads are complete.
Any help is appreciated. Here is my code:
submitForm() {
const user = firebase.auth().currentUser
const listingPostKey = firebase.database().ref('listings/').push().key
const listingRef = firebase.database().ref('listings/' + listingPostKey)
for (let i = 0; i < this.uploadedImages.length; i++) {
var storageRef = firebase.storage().ref('images/' + user.uid + '/' + this.imageName)
var uploadTask = storageRef.put(this.uploadedImages[i])
uploadTask.on('state_changed', (snapshot) => {
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100
}, error => {
this.errors.push(error.message)
}, () => {
// Upload complete
var downloadURL = uploadTask.snapshot.downloadURL
this.images_url.push(downloadURL)
this.createListing()
})
}
}
According to the docs:
put() and putString() both return an UploadTask which you can use as a promise, or use to manage and monitor the status of the upload.
So you can just use Promise.all to wait for all uploads to be done.
Here is the code I came up with:
submitForm() {
const user = firebase.auth().currentUser
const listingPostKey = firebase.database().ref('listings/').push().key
const listingRef = firebase.database().ref('listings/' + listingPostKey)
const storageRef = firebase.storage().ref('images/' + user.uid + '/' + this.imageName)
// map uploadedImages to array of uploadTasks (promises)
const uploads = this.uploadedImages.map(uploadedImage => {
const uploadTask = storageRef.put(uploadedImage)
// you probably don't need this part
// since 'progress' is not used anywhere
uploadTask.on('state_changed', snapshot => {
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100
})
return uploadTask.then(snapshot => {
this.images_url.push(snapshot.downloadURL)
})
})
// wait for all uploadTasks to be done
Promise.all(uploads).then(() => {
this.createListing()
})
}

Calculate MD5 hash of a large file using javascript

How do you upload a 500mb file and get a MD5 hash with CryptoJS?
Here is my code:
$('#upload-file').change(function(){
var reader = new FileReader();
reader.addEventListener('load',function () {
var hash = CryptoJS.MD5(CryptoJS.enc.Latin1.parse(this.result));
window.md5 = hash.toString(CryptoJS.enc.Hex);
});
reader.readAsBinaryString(this.files[0]);
});
If the file is under 200mb, it works. Anything bigger, this.result is an empty "".
I've tried:
filereader api on big files
javascript FileReader - parsing long file in chunks
and almost got this to work , but console is complaining about .join("")
http://dojo4.com/blog/processing-huge-files-with-an-html5-file-input
CryptoJS has a progressive api for hash digests. The rest is taken form alediaferia's answer with slight modifications.
function process() {
getMD5(
document.getElementById("my-file-input").files[0],
prog => console.log("Progress: " + prog)
).then(
res => console.log(res),
err => console.error(err)
);
}
function readChunked(file, chunkCallback, endCallback) {
var fileSize = file.size;
var chunkSize = 4 * 1024 * 1024; // 4MB
var offset = 0;
var reader = new FileReader();
reader.onload = function() {
if (reader.error) {
endCallback(reader.error || {});
return;
}
offset += reader.result.length;
// callback for handling read chunk
// TODO: handle errors
chunkCallback(reader.result, offset, fileSize);
if (offset >= fileSize) {
endCallback(null);
return;
}
readNext();
};
reader.onerror = function(err) {
endCallback(err || {});
};
function readNext() {
var fileSlice = file.slice(offset, offset + chunkSize);
reader.readAsBinaryString(fileSlice);
}
readNext();
}
function getMD5(blob, cbProgress) {
return new Promise((resolve, reject) => {
var md5 = CryptoJS.algo.MD5.create();
readChunked(blob, (chunk, offs, total) => {
md5.update(CryptoJS.enc.Latin1.parse(chunk));
if (cbProgress) {
cbProgress(offs / total);
}
}, err => {
if (err) {
reject(err);
} else {
// TODO: Handle errors
var hash = md5.finalize();
var hashHex = hash.toString(CryptoJS.enc.Hex);
resolve(hashHex);
}
});
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/core.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/md5.js"></script>
<input id="my-file-input" type="file">
<button onclick="process()">Process</button>
You don't need to read the whole file at once and feed it all in one go to CryptoJS routines.
You can create the hasher object, and feed chunks as you read them, and then get the final result.
Sample taken from the CryptoJS documentation
var sha256 = CryptoJS.algo.SHA256.create();
sha256.update("Message Part 1");
sha256.update("Message Part 2");
sha256.update("Message Part 3");
var hash = sha256.finalize();

Upload Base64 image with Firebase 3

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.

Categories