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);
}
);
});
}
Related
I want to upload multiple images to my firebase database at once. I will be needing HTML as well.
Here is what I have found by research, but I think due to not having correct HTML (maybe) it doesn't work.
fileButton.addEventListener('change', function(e){
//Get files
for (var i = 0; i < e.target.files.length; i++) {
var imageFile = e.target.files[i];
uploadImageAsPromise(imageFile);
}
});
//Handle waiting to upload each file using promise
function uploadImageAsPromise (imageFile) {
return new Promise(function (resolve, reject) {
var storageRef = firebase.storage().ref(fullDirectory+"/"+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;
uploader.value = percentage;
},
function error(err){
},
function complete(){
var downloadURL = task.snapshot.downloadURL;
}
);
});
}
<button type="button" id="fileButton">Upload</button>
<input id="imageFile" type="file" accept="image/*" multiple>
You can try and add console.log statements into your code and check if functions are being called. That helps a little.
Alright, so what I did here I was following this link. Basically, you listen to changes on your image input and push these files into an array. Then, when you submit the form, you have the array ready to be sent to firebase.
The snippet below is working fine :)
const fileCatcher = document.getElementById("fileCatcher");
const fileInput = document.getElementById("fileInput");
let fileList = [];
fileInput.addEventListener('change', function(e){
console.log('Input has changed');
fileList = []; //Reset the list
const files = fileInput.files;
for (var i = 0; i < files.length; i++) {
fileList.push(files[i]);
}
});
fileCatcher.addEventListener('submit', function(e){
e.preventDefault(); //Block the form from reloading the page
console.log(fileList);
const files = fileInput.files.list;
for (var i = 0; i < fileList.length; i++) {
var imageFile = fileList[i];
//uploadImageAsPromise(imageFile);
}
});
<form id = "fileCatcher">
<button type="submit">Upload</button>
<input id="fileInput" type="file" accept="image/*" multiple>
</form>
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'm new to Dropzone Js and i want to upload a file, process data to json then upload to my Flask server.
i appreciate any kind of help, thanks.
var id = '#kt_dropzone_4';
// set the preview element template
var previewNode = $(id + " .dropzone-item");
previewNode.id = "";
var previewTemplate = previewNode.parent('.dropzone-items').html();
previewNode.remove();
var myDropzone4 = new Dropzone(id, { // Make the whole body a dropzone
url: "/Upload", // Set the url for your upload script location
headers: {
'x-csrftoken': $('#csrf_Upload').val()
},
method: "post",
parallelUploads: 5,
acceptedFiles: ".xls, .xlsx, .csv",
previewTemplate: previewTemplate,
maxFilesize: 2, // Max filesize in MB
autoQueue: false, // Make sure the files aren't queued until manually added
previewsContainer: id + " .dropzone-items", // Define the container to display the previews
clickable: id +
" .dropzone-select" // Define the element that should be used as click trigger to select files.
});
myDropzone4.on("addedfile", function (file) {
// Hookup the start button
file.previewElement.querySelector(id + " .dropzone-start").onclick = function () {
myDropzone4.enqueueFile(file);
};
$(document).find(id + ' .dropzone-item').css('display', '');
$(id + " .dropzone-upload, " + id + " .dropzone-remove-all").css('display', 'inline-block');
//remove duplicates
if (this.files.length) {
var i, len;
for (i = 0, len = this.files.length; i < len - 1; i++) // -1 to exclude current file
{
if (this.files[i].name === file.name && this.files[i].size === file.size && this.files[i]
.lastModifiedDate.toString() === file.lastModifiedDate.toString()) {
this.removeFile(file);
$('#muted-span').text('Duplicates are not allowed').attr('class', 'kt-font-danger kt-font-bold').hide()
.fadeIn(1000)
setTimeout(function () {
$('#muted-span').hide().text('Only Excel and csv files are allowed for upload')
.removeClass('kt-font-danger kt-font-bold').fadeIn(500);
}, 2500);
}
}
}
});
// Update the total progress bar
myDropzone4.on("totaluploadprogress", function (progress) {
$(this).find(id + " .progress-bar").css('width', progress + "%");
});
myDropzone4.on("sending", function (file, response) {
console.log(file)
console.log(response)
// Show the total progress bar when upload starts
$(id + " .progress-bar").css('opacity', '1');
// And disable the start button
file.previewElement.querySelector(id + " .dropzone-start").setAttribute("disabled", "disabled");
});
// Hide the total progress bar when nothing's uploading anymore
myDropzone4.on("complete", function (progress) {
var thisProgressBar = id + " .dz-complete";
setTimeout(function () {
$(thisProgressBar + " .progress-bar, " + thisProgressBar + " .progress, " + thisProgressBar +
" .dropzone-start").css('opacity', '0');
}, 300)
});
// Setup the buttons for all transfers
document.querySelector(id + " .dropzone-upload").onclick = function () {
myDropzone4.enqueueFiles(myDropzone4.getFilesWithStatus(Dropzone.ADDED));
};
// Setup the button for remove all files
document.querySelector(id + " .dropzone-remove-all").onclick = function () {
$(id + " .dropzone-upload, " + id + " .dropzone-remove-all").css('display', 'none');
myDropzone4.removeAllFiles(true);
};
// On all files completed upload
myDropzone4.on("queuecomplete", function (progress) {
$(id + " .dropzone-upload").css('display', 'none');
});
// On all files removed
myDropzone4.on("removedfile", function (file) {
if (myDropzone4.files.length < 1) {
$(id + " .dropzone-upload, " + id + " .dropzone-remove-all").css('display', 'none');
}
});
I have not found yet a way to get the uploaded data from dropzonejs. I tried to read the file with FileReader but it's not a binary data (correct me if i'm wrong).
I need to process data on myDropzone4.on("addedfile", function (file){})
and return it as a json format if possible.
I found an answer for it, I just needed to find the input type file.when using dropzone.js either you find the input type file in the html page or in their javascript file, where i found that the input type file was being created with a class to hide this element :
var setupHiddenFileInput = function setupHiddenFileInput() {
if (_this3.hiddenFileInput) {
_this3.hiddenFileInput.parentNode.removeChild(_this3.hiddenFileInput);
}
_this3.hiddenFileInput = document.createElement("input");
_this3.hiddenFileInput.setAttribute("type", "file");
_this3.hiddenFileInput.setAttribute("id", "123");
if (_this3.options.maxFiles === null || _this3.options.maxFiles > 1) {
_this3.hiddenFileInput.setAttribute("multiple", "multiple");
}
// _this3.hiddenFileInput.className = "dz-hidden-input";
}
so i gave it an id and bind an event to the input then i read the file with two functions depends on the format of the file uploaded, for csv files to json :
function getText(fileToRead) {
var reader = new FileReader();
reader.readAsText(fileToRead);
reader.onload = loadHandler;
reader.onerror = errorHandler;
}
function loadHandler(event) {
var csv = event.target.result;
process(csv);
}
function process(csv) {
// Newline split
var lines = csv.split("\n");
result = [];
var headers = lines[0].split(",");
for (var i = 1; i < lines.length - 1; i++) {
var obj = {};
//Comma split
var currentline = lines[i].split(",");
for (var j = 0; j < headers.length; j++) {
obj[headers[j]] = currentline[j];
}
result.push(obj);
}
console.log(result);
}
function errorHandler(evt) {
if (evt.target.error.name == "NotReadableError") {
alert("Canno't read file !");
}
}
Read excel files (xls,xlsx) format to json format:
var ExcelToJSON = function () {
this.parseExcel = function (file) {
var reader = new FileReader();
reader.onload = function (e) {
var data = e.target.result;
var workbook = XLSX.read(data, {
type: 'binary'
});
workbook.SheetNames.forEach(function (sheetName) {
// Here is your object
var XL_row_object = XLSX.utils.sheet_to_row_object_array(workbook.Sheets[
sheetName]);
var json_object = JSON.stringify(XL_row_object);
console.log(JSON.parse(json_object));
jQuery('#xlx_json').val(json_object);
})
};
reader.onerror = function (ex) {
console.log(ex);
};
reader.readAsBinaryString(file);
};
};
the event that will detect change on the input, detect file format then use one of those to get the result in a JSON format:
$(document).ready(function () {
$('input[type="file"]').on('change', function (e) {
// EXCEL TO JSON
var files = e.target.files;
console.log(files)
var xl2json = new ExcelToJSON();
xl2json.parseExcel(files[0]);
var fileName = e.target.files[0].name;
console.log('The file "' + fileName + '" has been selected.');
// CSV TO JSON
var files = e.target.files;
if (window.FileReader) {
getText(files[0]);
} else {
alert('FileReader are not supported in this browser.');
}
});
});
I hope this helps i'm using dropzonejs with keenthemes implementation.
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');
});
});
I am trying to use firebase to upload a file as well as enter the file in my data base at the same time. (I would like to keep track of how many uploads there are so I can rename the file to something unique) I successfully uploaded, and also put a copy in the database but I can not get a number back of how many files are in the child folder using the once() and getChildren() methods. I keep getting an error. What am I doing wrong? The count number error is coming from here:
var ref = firebase.database();
ref.once("value")
.then(function(snapshot) {
var numberSamples = snapshot.child(sampleType).numChildren(); //number of children
});
Full code:
<script src="https://www.gstatic.com/firebasejs/3.3.2/firebase.js"></script>
<script>
// Initialize Firebase
var config = {
apiKey: "AIzaSyC9jUMxOvvdgcBvXecvpRiactG6ffbwTgg",
authDomain: "instasample-d8eea.firebaseapp.com",
databaseURL: "https://instasample-d8eea.firebaseio.com",
storageBucket: "instasample-d8eea.appspot.com",
};
firebase.initializeApp(config);
//remove error on click
function removeSampleClass() {
$("#sampleType").removeClass('sampleTypeError')
$("#sampleType").addClass('sampleType')
}
function removeFileClass() {
$("#fileButton").removeClass('fileButtonError')
$("#fileButton").addClass('fileButton')
}
// upload files and check for errors
function uploadFile(){ //on GO
//get elements
var uploader = document.getElementById('uploader')
var fileButton = document.getElementById('fileButton')
//var file = fileButton.target.files[0]; //get file
var sampleType = document.getElementById('sampleType').options[document.getElementById('sampleType').selectedIndex].text; //get folder location
var file = fileButton.files[0]; //get file
if (sampleType == "Select Type" && fileButton.value == "") { //check both selection
$("#sampleType").addClass('sampleTypeError')
$("#sampleType").removeClass('sampleType')
$("#fileButton").addClass('fileButtonError')
$("#fileButton").removeClass('fileButton')
}
else if (fileButton.value == "") { //check file selection
$("#fileButton").addClass('fileButtonError')
$("#fileButton").removeClass('fileButton')
}
else if (sampleType == "Select Type") { //check sample selection
$("#sampleType").addClass('sampleTypeError')
$("#sampleType").removeClass('sampleType')
}
else{ //upload if good
uploader.classList.remove('hide'); //unhide progress bar
//create storage reference with sampleType
var storageRef = firebase.storage().ref("/" + sampleType + "/" + file.name);
//upload file
var task = storageRef.put(file);
//update progress bar
task.on('state_changed',
function progress(snapshot){
var percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
uploader.value = percentage;
},
function error() {
alert('There was a problem uploading your file')
},
function complete() {
//get root reference
const dbRefType = firebase.database().ref().child(sampleType);
var ref = firebase.database();
ref.once("value")
.then(function(snapshot) {
var numberSamples = snapshot.child(sampleType).numChildren(); // 2 ("first", "last")
});
var updates = {};
updates["/" + sampleType + "/" + sampleType + "Sample" + numberSamples ] = numberSamples;
return firebase.database().ref().update(updates);
//sync data and return children of sampleType to console
//dbRefType.on('value', snap => console.log(snap.val()));
//create new reference to file
//var sampleFileRef = dbRefType.child("sample");
//TODO: list how many are in list
//add new file to list
}
);
}
}
</script>
nevermind, I got it. used
dbRefType.once('value', function(snapshot) { alert('Count: ' + snapshot.numChildren()); });