Firebase storage - some of downloadURLs is null - javascript

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

Related

Firebase real-time database posting over old images with JavaScript

I'm trying to make a simple web page just to play around with Firebase. I'm currently working on users uploading their photos and storing them as well as a reference to them in the database. I had a working version except the only issue was that if multiple users opened the page at the same time, only the most recent post would last. I wanted to use the realtime function to overcome this and have come up with this.
var postRef = firebase.database().ref('variables/postNumber');
postRef.on('value',function(snapshot) {
var postName = snapshot.val();
var uploader = document.getElementById('uploader');
var filebutton = document.getElementById('filebutton');
// get file
filebutton.addEventListener('change', function(e) {
var file = e.target.files[0];
var ext = file.name.split('.').pop();;
console.log(postName);
console.log(ext);
//create a storage ref
var storageRef = firebase.storage().ref('posts/' + postName + "." + ext);
var task = storageRef.put(file);
publishPost(postName, ext);
function publishPost(postName, ext) {
firebase.database().ref('posts/' + postName).set({
postID: postName,
postDate: Date(),
fileType : ext
});
firebase.database().ref('variables/').set({
postNumber: postName + 1
});
}
task.on('state_changed',
function progress(snapshot){
var percentage = (snapshot.bytesTransferred / snapshot.totalBytes) *100;
uploader.value = percentage;
},
function error(err){
},
function complete(postName, ext){
uploader.value = 0;
window.alert('Your meme Uploaded correctly');
},
);
});
});
This works well, always updating the postName variable except when a new user posts, it will rewrite every post to the new post. For example, if user A posts a picture while user B was already on the page, then when user B posts, his post will upload twice the first time overriding user A's post. Can anyone shed some light on why this is happening? I was thinking of moving the listener to start the function but not sure if thats the right choice.
What happens is that the event listener is attached to the button every time new value is detected. Which means that the change event listener on filebutton cannot be in the observer at all.
Working code:
let postName = null;
var postRef = firebase.database().ref('variables/postNumber');
postRef.on('value', function(snapshot) {
const value = snapshot.val();
if (value === null) {
// Handle error when no value was returned
return;
}
postName = value;
});
var uploader = document.getElementById('uploader');
var filebutton = document.getElementById('filebutton');
filebutton.addEventListener('change', function(e) {
if (postName === null) {
// Handle the case then post name is still null (either wan't loaded yet or couldn't be loaded)
return;
}
var file = e.target.files[0];
var ext = file.name.split('.').pop();;
//create a storage ref
var storageRef = firebase.storage().ref('posts/' + postName + "." + ext);
var task = storageRef.put(file);
publishPost(postName, ext);
function publishPost(postName, ext) {
firebase.database().ref('posts/' + postName).set({
postID: postName,
postDate: Date(),
fileType : ext
});
firebase.database().ref('variables/').set({
postNumber: postName + 1
});
}
task.on('state_changed',
function progress(snapshot){
var percentage = (snapshot.bytesTransferred / snapshot.totalBytes) *100;
uploader.value = percentage;
},
function error(err){
},
function complete(postName, ext){
uploader.value = 0;
window.alert('Your meme Uploaded correctly');
});
});

Preview image before sending it out

Somwhere around i found code that works fine for drag and drop:
addImage($event) {
// loading the FileList from the dataTransfer
let dataTransfer: DataTransfer = $event.mouseEvent.dataTransfer;
if (dataTransfer && dataTransfer.files) {
// needed to support posting binaries and usual form values
let files: FileList = dataTransfer.files;
// uploading the files one by one asynchrounusly
for (let i = 0; i < files.length; i++) {
let file: File = files[i];
// just for debugging
console.log('Name: ' + file.name + '\n Type: ' + file.type + '\n Size: ' + file.size + '\n Date: ' + file.lastModifiedDate);
// collecting the data to post
let data = new FormData();
data.append('file', file);
data.append('fileName', file.name);
data.append('fileSize', file.size.toString());
data.append('fileType', file.type);
data.append('fileLastMod', file.lastModifiedDate);
// posting the data
let url = 'http://***********.com/gallery/' + this.selectedCategory;
this._http.post(url, data)
.toPromise()
.catch(reason => {
console.log(JSON.stringify(reason));
}).then(result => {
this.getImages();
});
}
}
}
But before i send images out, i want them to preview in modal form.
I was thinking about function that will that take 1 param to works, like:
previewFile(url) {
let elem = document.createElement("img");
elem.className = "previewImage";
elem.src = url
document.getElementById("previewImages").appendChild(elem);
}
But how to get the URL ?
Use this code:
elem.src = URL.createObjectURL(file);

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()
})
}

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.

Capture video and store it on the server?

I try to capture a video with Apache Cordova Plugin cordova-plugin-media-capture. How can i send this video to my server and store it?
This is an official example on how to start capturing a video:
// capture callback
var captureSuccess = function(mediaFiles) {
var i, path, len;
for (i = 0, len = mediaFiles.length; i < len; i += 1) {
path = mediaFiles[i].fullPath;
// do something interesting with the file
}
};
// capture error callback
var captureError = function(error) {
navigator.notification.alert('Error code: ' + error.code, null, 'Capture Error');
};
// start video capture
navigator.device.capture.captureVideo(captureSuccess, captureError, {limit:2, duration: 10});
But how can i send the video to my server to store it?
What do i have to pass to my ajax code?
// capture callback
var captureSuccess = function(mediaFiles)
{
var i, path, len;
for (i = 0, len = mediaFiles.length; i < len; i += 1)
{
path = mediaFiles[i].fullPath;
$.ajax
(
"ajax.php",
{
type: "POST",
data: { path: path } //This will just send the path to the server
}
);
}
};
Better to use this Function.
function uploadFile(mediaFile) {
var ft = new FileTransfer(),
path = mediaFile.fullPath,
name = mediaFile.name;
var options = new FileUploadOptions();
options.mimeType = "video/mpeg";
options.fileName = name;
options.chunkedMode = true;
ft.upload(path,
"**Your WebService Url Goes Here**",
function(result) {
console.log('Upload success: ' + result.responseCode);
console.log(result.bytesSent + ' bytes sent');
},
function(error) {
console.log('Error uploading file ' + path + ': ' + error.code);
},
options);
}
Call this function where you are getting your File Path.
Like Below.
uploadFile(mediaFiles[i]);
EDIT 1
Note: Make sure you added all below plugins in your project.
cordova-plugin-media
cordova-plugin-media-capture
cordova-plugin-file
cordova-plugin-file-transfer

Categories