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()
})
}
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'm trying to upload multiple files simultaneously to Azure BLOB storage from JavaScript. I'm not sure how it's handling the parallelism, but I'm trying to have separate progress bars for each file/upload/promise.
Now the progress function gets called but gives only "loadedBytes" I need a way to know which progress bar to update.. One person suggested onload give an identifier, it does not seem to have an onload event. When I use the code below, the index is always the last one in the loop.
try {
console.log("Uploading files…");
var inputElement = document.getElementById('fileSelector');
const promises = [];
for (var fileIndex = 0; fileIndex < inputElement.files.length; fileIndex++) {
const file = inputElement.files[fileIndex];
var thisToken = await this.Instance.invokeMethodAsync('jsGetSASToken', file.name);
var containerURL = new azblob.ContainerURL(thisToken, azblob.StorageURL.newPipeline(new azblob.AnonymousCredential));
const blockBlobURL = azblob.BlockBlobURL.fromContainerURL(containerURL, file.name);
var blobUploadOptions = {
blockSize: 4 * 1024 * 1024, // 4MB block size
parallelism: 20, // 20 concurrency
metadata: { 'testindex': fileIndex.toString() },
progress: function (ev) {
var percentdone = ((ev.loadedBytes / file.size) * 100);
// Jumps around because loadedBytes is different for each upload
document.getElementById('percentdone-' + fileIndex).innerHTML = percentdone.toFixed(2) + "%";
// fileIndex is always the last item in the loop
}
};
promises.push(
azblob.uploadBrowserDataToBlockBlob(
azblob.Aborter.none,
file,
blockBlobURL,
blobUploadOptions
)
);
}
await Promise.all(promises);
console.log('Done.');
} catch (error) {
console.log("File Upload Error");
console.log(error);
}
Seems this issue is caused by fileIndex. I use file.name as the identifier, everything works as excepted. Try the code below:
<html>
<body>
<button id="select-button">Select and upload files</button>
<input type="file" id="file-input" multiple style="display: none;" />
<div id="showProgress"></div>
<p><b>Status:</b></p>
<p id="status" style="height:160px; width: 593px; overflow: scroll;" />
</body>
<script src="./azure-storage-blob.js" charset="utf-8"></script>
<script>
const selectButton = document.getElementById("select-button");
const fileInput = document.getElementById("file-input");
const status = document.getElementById("status");
const reportStatus = message => {
status.innerHTML += `${message}<br/>`;
status.scrollTop = status.scrollHeight;
}
const accountName = "storage account";
const sasString = "sas token";
const containerName = "container";
const containerURL = new azblob.ContainerURL(
`https://${accountName}.blob.core.windows.net/${containerName}?${sasString}`,
azblob.StorageURL.newPipeline(new azblob.AnonymousCredential));
const uploadFiles = async () => {
try {
reportStatus("Uploading files...");
const promises = [];
for (var fileIndex = 0; fileIndex < fileInput.files.length; fileIndex++) {
const file = fileInput.files[fileIndex];
const blockBlobURL = azblob.BlockBlobURL.fromContainerURL(containerURL, file.name);
document.getElementById('showProgress').innerHTML += file.name +":<div id='progress-"+ file.name +"'></div>"
var blobUploadOptions = {
blockSize: 4 * 1024 * 1024, // 4MB block size
parallelism: 20, // 20 concurrency
metadata: { 'testindex': fileIndex.toString() },
progress: function (ev) {
var percentdone = ((ev.loadedBytes / file.size) * 100);
var progessItem = document.getElementById('progress-' + file.name);
progessItem.innerHTML = percentdone.toFixed(2) + "%";
}
};
var promise = azblob.uploadBrowserDataToBlockBlob(
azblob.Aborter.none, file, blockBlobURL,blobUploadOptions);
promise.then((result)=>{
var progessItem = document.getElementById('progress-' + file.name);
progessItem.innerHTML += " file link"
});
promises.push(promise);
}
await Promise.all(promises);
reportStatus("Done.");
} catch (error) {
console.log(error)
}
}
selectButton.addEventListener("click", () => fileInput.click());
fileInput.addEventListener("change", uploadFiles);
</script>
</html>
Result:
Update Result:
I can show one progress percentage when uploading multiple files.How to separate progress percentage if I uploading multiple files?
//multi upload
fileuploadHandler = () => {
const storageRef = fire.storage().ref();
this.state.file.forEach((file) => {
storageRef
.child(`images/${file.name}`)
.put(file).then((snapshot) => {
var uploadTask = storageRef.child(`images/${file.name}`).put(file);
uploadTask.on('state_changed', (snapshot) =>{
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
var fixprogress = progress.toFixed(2);
this.setState({fixprogress});
console.log('Upload is ' + fixprogress + '% done');
})
})
});
}
and this my button and show progress percentage.
<button className="loginBtn2 loginBtn--U" onClick={this.fileuploadHandler}> Upload!</button>
uploading {this.state.fixprogress}
Use the second parameter passed to forEach and you get an index. Use this to identify the different files and display them somehow differently, like this:
//multi upload
fileuploadHandler = () => {
const storageRef = fire.storage().ref();
// USE THE SECOND PARAMETER: INDEX
this.state.file.forEach( (file, index) => {
storageRef
.child(`images/${file.name}`)
.put(file).then((snapshot) => {
var uploadTask = storageRef.child(`images/${file.name}`).put(file);
uploadTask.on('state_changed', (snapshot) =>{
var progress = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
var fixprogress = progress.toFixed(2);
// NOW USE IT TO DISPLAY SPECIFIC STATE FOR THAT FILE
this.setState( { progress: { ( "file" + index ) : fixprogress} } );
// LOG THE STATE
console.log('Upload for file number #' + index + ' is ' + fixprogress + '% done');
})
})
});
}
In this case you then retrieve the this.state.progress.file0 to get the progress of the first file.
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');
});
});
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 = ...