I'm trying to create a multi-file upload system, however the length property of the fileInput.files.length is undefined.
Uncaught TypeError: Cannot read property 'length' of undefined
I have tried adding and removing the square brackets from document.getElementById("file1[]")
Assigning fileInput.files to another variable and calling thatVariable.length
Both did not work.
Since this is a multi file upload system, I need it to be in an array.
HTML CODE:
<form action='/' method='post' enctype="multipart/form-data" id='file'>
<button type="button" onclick="document.getElementById('file1').click(); return false;" class="btn btn-primary" id='choosefile'><span class='glyphicon glyphicon-open-file'></span> Choose File</button><br>
<b id="filename"></b><br>
<input type="text" placeholder="New File Name" id="fileplaceholder">
<input type="file" id="file1" name="file1[]" style="visibility: hidden" onchange="filenameprint()" multiple>
<button type="button" onclick="uploadCloud()" class='btn btn-success' id='uploadfile'><span class="glyphicon glyphicon-upload"></span> Upload File</button><br>
<br>
<div class="progress">
<div class="progress-bar progress-bar-striped active" role="progressbar" aria-valuenow="40"
aria-valuemin="0" aria-valuemax="1" style="width:0%" id='progress'>
<span id='uploadpercent'></span>
</div>
</div>
<span id='loaded'></span>
<script>
function filenameprint() {
var file1 = document.getElementById('file1').value;
if (!empty(file1)) {
document.getElementById('filename').innerHTML = file1;
} else {
document.getElementById('filename').innerHTML = "No File Chosen"
}
}
</script>
</form>
Javascript Code:
function uploadCloud() {
//Sets the Progress Bar to 0
_('progress').style.width = "0%";
//Get's the Upload File Button Object Reference
var fileInput = document.getElementsByName("file1[]");
var formData = false;
//Declares the Form Data Object
if (window.FormData) {
formData = new FormData();
}
var file, reader;
console.log((fileInput.files).length);
for (var i = 0; i < fileInput.files.length; i++) {//ERROR COMES FROM HERE!!!
file = fileInput.files[i];
if (window.FileReader) {
reader = new FileReader();
reader.onloaded = function (e) {
}
reader.readAsDataURL(file);
}
if (formData) {
formData.append('file1', file);
}
}
if (formData) {
$.ajax({
url: '/uploadCloud.php', //Server script to process data
type: 'POST',
// Form data
data: formData,
//Options to tell jQuery not to process data or worry about content-type.
cache: false,
contentType: false,
processData: false,
xhr: function () { // Custom XMLHttpRequest
var myXhr = $.ajaxSettings.xhr();
if (myXhr.upload) { // Check if upload property exists
myXhr.upload.addEventListener('progress', progressHandlingFunction, false); // For handling the progress of the upload
}
console.log(myXhr);
return myXhr;
},
beforeSend: function () {
_('uploadfile').setAttribute('disabled', 'disabled');
_('choosefile').setAttribute('disabled', 'disabled');
},
//Ajax events
success: function (data) {
if (data == 0) {
_('loaded').innerHTML = "";
_('progress').style.width = "0%";
_('filename').innerHTML = "<b>No File</b>"
} else {
_("filename").innerHTML = data;
}
_('uploadpercent').innerHTML = "";
_('loaded').innerHTML = "";
_('uploadfile').removeAttribute('disabled');
_('choosefile').removeAttribute('disabled');
_('progress').style.width = "0%";
},
});
function progressHandlingFunction(e) {
if (e.lengthComputable) {
_('progress').style.width = (e.loaded / e.total) * 100 + "%";
_('uploadpercent').innerHTML = Math.round((e.loaded / e.total) * 100) + "% complete (" + _('filename').innerHTML + ")";
_('loaded').innerHTML = "Upload " + Math.round((e.loaded / e.total) * 100) + "% complete [" + e.loaded + " bytes loaded of " + e.total + " bytes total]";
}
}
} else {
_("filename").innerHTML = "<b>No File</b>"
}
}
Because
var fileInput = document.getElementsByName("file1[]");
is a collection and you act like it is a single element. You need to reference the individual elements.
fileInput[0].files
Related
I would like to know how I can return the name of the folder created in the folder variable outside the createOrGetFolder function, the intention is to be able to create a file with the same name as the folder created, in this code here:
const saveDataAsCSV = (data, folderId) => DriveApp.getFolderById(folderId).createFile(folder, data);
This is my complete code from the .gs file:
/**
* Modified script written by Tanaike and CharlesPlucker
*
* Additional Script by Tyrone
* version 20.01.2023.1
*/
function doGet(e) {
return HtmlService.createTemplateFromFile('forms0101.html').evaluate();
}
function getOAuthToken() {
return ScriptApp.getOAuthToken();
}
function getParent(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var id = ss.getId();
var parent = DriveApp.getFileById(id).getParents().next().getId();
return parent
}
function getLimitFolder(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var pastapai = DriveApp.getFileById(ss.getId()).getParents();
var limitfolder = pastapai.next().getFoldersByName("_").next().getId();
return limitfolder
}
/**
* creates a folder under a parent folder, and returns it's id. If the folder already exists
* then it is not created and it simply returns the id of the existing one
*/
function createOrGetFolder(folderName, parentFolderId) {
try {
var parentFolder = DriveApp.getFolderById(parentFolderId), folder;
if (parentFolder) {
var foldersIter = parentFolder.getFoldersByName("Video");
if (foldersIter.hasNext()) {
var videoFolder = foldersIter.next();
var nextFolderName = folderName + "-01";
while (!folder) {
video_folder = videoFolder.getFoldersByName(nextFolderName);
if (video_folder.hasNext()) {
folder = video_folder.next();
var files = folder.getFiles();
if (files.hasNext()) {
var [a, b] = nextFolderName.split("-");
nextFolderName = `${a}-${String(Number(b) + 1).padStart(2, "0")}`;
folder = null;
}
} else {
folder = videoFolder.createFolder(nextFolderName);
}
}
} else {
folder = parentFolder.createFolder("Video");
folder = folder.createFolder(folderName);
}
} else {
throw new Error("Parent Folder with id: " + parentFolderId + " not found");
}
return folder.getId();
} catch (error) {
return error;
}
}
const saveDataAsCSV = (data, folderId) => DriveApp.getFolderById(folderId).createFile("Sample.csv", data);
// NOTE: always make sure we use DriveApp, even if it's in a comment, for google to import those
// libraries and allow the rest of the app to work. see https://github.com/tanaikech/Resumable_Upload_For_WebApps
Note that in const saveDataAsCSV the currently file creation name is Sample.csv, and this is where I want to apply the folder variable that is in the function createOrGetFolder(folderName, parentFolderId)
And this is the complete code of the HTML file:
/**
* Modified script written by Tanaike and CharlesPlucker
*
* Additional Script by Tyrone
* version 20.01.2023.1
*/
<!DOCTYPE html>
<html>
<head>
<base target="_blank">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Drive Multi Large File Upload</title>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.5/css/materialize.min.css">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<style>
#import url('https://fonts.googleapis.com/css2?family=Rubik:wght#400;600;700&display=swap');
.disclaimer{
width: 480px;
color: #646464;
margin: 20px auto;
padding:0 16px;
text-align:center;
font:400 12px Rubik,sans-serif;
}
h5.center-align.teal-text {
font:700 26px Rubik,sans-serif;
color: #00F498!important;
}
.row {
font:600 14px Rubik,sans-serif;
}
.btn {
background-color: black;
}
.btn:hover {
background-color: #00F498;
}
body {
margin-top: -40px;
}
#progress {
color: #00000;
}
.disclaimer a{
color: #00BCAA;
}
#credit{
display:none
}
</style>
</head>
<body>
<form class="main" id="form" novalidate="novalidate" style="max-width: 480px;margin: 40px auto;">
<div id="forminner">
<h5 class="center-align teal-text" style="margin-bottom: -10px; font-size: 20px; font-family: Rubik; ">YOUR NAME</h5>
<div class="row">
<div class="input-field col s12">
<input id="name01" type="text" name="Name" class="validate" required="required" aria-required="true">
<label for="name" class="">Name</label>
</div>
</div>
<h5 class="center-align teal-text" style="margin-bottom: -10px; font-size: 20px; font-family: Rubik; ">SOME DESCRIPTION</h5>
<div class="row">
<div class="input-field col s12">
<input id="description" type="text" name="Description" class="validate" required="required" aria-required="true">
<label for="name">Description</label>
</div>
</div>
<div class="row">
<div class="col-8 col-md-4">
<h6>Model</h6>
<select class="custom-select" id="Model">
<option selected="">Choose...</option>
<option value="01">01</option>
<option value="02">02</option>
<option value="03">03</option>
</select>
<h6>Color</h6>
<select class="custom-select" id="Color">
<option selected="">Choose...</option>
<option value="Red">Red</option>
<option value="Green">Green</option>
</select>
</div>
</div>
<div class="row">
<div class="col s12">
<h5 class="center-align teal-text">Upload the Video File</h5>
</div>
</div>
<div class="row">
<div class="file-field input-field col s12">
<div id="input-btn" class="btn">
<span>File</span>
<input id="files" type="file" single="">
</div>
<div class="file-path-wrapper">
<input class="file-path validate" type="text" placeholder="Select the file">
</div>
</div>
</div>
<div class="row">
<div class="input-field col s6">
<button id="submit-btn" class="waves-effect waves-light btn submit-btn" type="submit" onclick="submitForm(); return false;">Submit</button>
</div>
</div>
<div class="row">
<div class="input-field col s12 hide" id="update">
<hr>
<p>
Por favor, aguarde enquanto seu arquivo está sendo carregado.<br><span style="color: #00000;"><b>Não feche ou atualize a janela durante o upload.</b></span>
</p>
</div>
</div>
<div class="row">
<div class="input-field col s12" id="progress">
</div>
</div>
</div>
</div>
<div id="success" style="display:none">
<h5 class="center-align teal-text">Tudo certo!</h5>
<p>Se você já preencheu todos os campos é só fechar essa janela e clicar em enviar!</p>
<button id="fechar" class="waves-effect waves-light btn submit-btn" style ="transform: translateX(160%);" type="button" onclick="google.script.host.close()">Fechar</button>
</div>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/0.97.5/js/materialize.min.js"></script>
<script src="https://gumroad.com/js/gumroad.js"></script>
<script>
var upload_folder = "01";
const chunkSize = 5242880;
const uploadParentFolderId = <?=getParent()?>; // creates a folder inside of this folder
const limitfolder = <?=getLimitFolder()?>;
function closer(){
google.script.host.close();
}
function submitForm() {
// Added the below script.
if ($('#submit-btn.disabled')[0]) return; // short circuit
var name = upload_folder
var files = [...$('#files')[0].files]; // convert from FileList to array
if (files.length === 0) {
showError("Por favor, selecione um arquivo");
return;
}
var name = $('#name01').val();
var description = $('#description').val();
var model = $('#Model').val();
upload_folder = model;
var color = $('#Color').val();
var form_values = [name, description, model, color];
form_values = form_values.map(r => r.replaceAll(",", "#")); // Essa linha substitui todas as "," por "#" antes de gerar o .csv
var data = form_values.join(",");
google.script.run.saveDataAsCSV(data, uploadParentFolderId);
google.script.run.saveDataAsCSV(data, limitfolder);
disableForm(); // prevent re submission
// the map and reduce are here to ensure that only one file is uploaded at a time. This allows
// the promises to be run sequentially
files.map(file => uploadFilePromiseFactory(file))
.reduce((promiseChain, currentTask) => {
return promiseChain.then(currentTask);
}, Promise.resolve([])).then( () => {
console.log("Completed all files upload");
showSuccess();
});
}
function disableForm() {
$('#submit-btn').addClass('disabled');
$('#input-btn').addClass('disabled');
$('#update').removeClass('hide');
$('#update').removeClass('hide');
}
function uploadFilePromiseFactory(file) {
return () => {
console.log("Processing: ", file.name);
return new Promise((resolve, reject) => {
showProgressMessage("Seu arquivo está sendo carregado");
var fr = new FileReader();
fr.fileName = file.name;
fr.fileSize = file.size;
fr.fileType = file.type;
// not sure of a better way of passing the promise functions down
fr.resolve = () => resolve();
fr.reject = (error) => reject(error);
fr.onload = onFileReaderLoad;
fr.readAsArrayBuffer(file);
});
};
}
/**
* Gets called once the browser has loaded a file. The main logic that creates a folder
* and initiates the file upload resides here
*/
function onFileReaderLoad(onLoadEvent) {
var fr = this;
var newFolderName = upload_folder
createOrGetFolder(newFolderName, uploadParentFolderId).then(newFolderId => {
console.log("Found or created guest folder with id: ", newFolderId);
uploadFileToDriveFolder.call(fr, newFolderId).then(() => {
fr.resolve();
}, (error) => {
fr.reject(error);
});
},
(error) => {
if (error) {
showError(error.toString());
}
console.log("onFileReaderLoad Error2: ", error);
});
}
/**
* call to the DriveApp api. Wrapped in a promise in case I want to address timing issues between a
* createFolder and findFolderById
*/
function createOrGetFolder(folderName, parentFolderId) {
return new Promise((resolve, reject) => {
google.script.run.withSuccessHandler(response => {
console.log("createOrGetFolder response: ", response);
if (response && response.length) {
resolve(response);
}
reject(response);
}).createOrGetFolder(folderName, parentFolderId);
});
}
/**
* Helper functions modified from:
* https://github.com/tanaikech/Resumable_Upload_For_WebApps
*/
function uploadFileToDriveFolder(parentFolderId) {
var fr = this;
return new Promise((resolve, reject) => {
var fileName = fr.fileName;
var fileSize = fr.fileSize;
var fileType = fr.fileType;
console.log({fileName: fileName, fileSize: fileSize, fileType: fileType});
var buf = fr.result;
var chunkpot = getChunkpot(chunkSize, fileSize);
var uint8Array = new Uint8Array(buf);
var chunks = chunkpot.chunks.map(function(e) {
return {
data: uint8Array.slice(e.startByte, e.endByte + 1),
length: e.numByte,
range: "bytes " + e.startByte + "-" + e.endByte + "/" + chunkpot.total,
};
});
google.script.run.withSuccessHandler(oAuthToken => {
var xhr = new XMLHttpRequest();
xhr.open("POST", "https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable");
xhr.setRequestHeader('Authorization', "Bearer " + oAuthToken);
xhr.setRequestHeader('Content-Type', "application/json");
xhr.send(JSON.stringify({
mimeType: fileType,
name: fileName,
parents: [parentFolderId]
}));
xhr.onload = () => {
doUpload(fileName, {
location: xhr.getResponseHeader("location"),
chunks: chunks,
}).then(success => {
resolve(success);
console.log("Successfully uploaded: ", fileName);
},
error => {
reject(error);
});
};
xhr.onerror = () => {
console.log("ERROR: ", xhr.response);
reject(xhr.response);
};
}).getOAuthToken();
});
}
function showSuccess() {
$('#forminner').hide();
$('#success').show();
$('#fechar').show();
}
function showError(e) {
$('#progress').addClass('red-text').html(e);
}
function showMessage(e) {
$('#update').html(e);
}
function showProgressMessage(e) {
$('#progress').removeClass('red-text').html(e);
}
/**
* Helper functions modified from:
* https://github.com/tanaikech/Resumable_Upload_For_WebApps
*/
function doUpload(fileName, e) {
return new Promise((resolve, reject) => {
showProgressMessage("Carregando: <span style='color: #00F498 ;'>" + "0%</span>");
var chunks = e.chunks;
var location = e.location;
var cnt = 0;
var end = chunks.length;
var temp = function callback(cnt) {
var e = chunks[cnt];
var xhr = new XMLHttpRequest();
xhr.open("PUT", location, true);
console.log("content range: ", e.range);
xhr.setRequestHeader('Content-Range', e.range);
xhr.send(e.data);
xhr.onloadend = function() {
var status = xhr.status;
cnt += 1;
console.log("Uploading: " + status + " (" + cnt + " / " + end + ")");
showProgressMessage("Carregando: <span style='color: #00F498 ;'>"
+ Math.floor(100 * cnt / end) + "%</span>" );
if (status == 308) {
callback(cnt);
} else if (status == 200) {
$("#progress").text("Done.");
resolve();
} else {
$("#progress").text("Error: " + xhr.response);
reject();
}
};
}(cnt);
});
}
/**
* Helper functions modified from:
* https://github.com/tanaikech/Resumable_Upload_For_WebApps
*/
function getChunkpot(chunkSize, fileSize) {
var chunkPot = {};
chunkPot.total = fileSize;
chunkPot.chunks = [];
if (fileSize > chunkSize) {
var numE = chunkSize;
var endS = function(f, n) {
var c = f % n;
if (c == 0) {
return 0;
} else {
return c;
}
}(fileSize, numE);
var repeat = Math.floor(fileSize / numE);
for (var i = 0; i <= repeat; i++) {
var startAddress = i * numE;
var c = {};
c.startByte = startAddress;
if (i < repeat) {
c.endByte = startAddress + numE - 1;
c.numByte = numE;
chunkPot.chunks.push(c);
} else if (i == repeat && endS > 0) {
c.endByte = startAddress + endS - 1;
c.numByte = endS;
chunkPot.chunks.push(c);
}
}
} else {
var chunk = {
startByte: 0,
endByte: fileSize - 1,
numByte: fileSize,
};
chunkPot.chunks.push(chunk);
}
return chunkPot;
}
</script>
</body>
</html>
As folder is without the var prefix, I figured it should work, as in theory this makes it a global variable... however I still get the folder is undefined message in the console.
I also tried calling the function before the file creation code, like this:
createOrGetFolder(folderName, parentFolderId);
const saveDataAsCSV = (data, folderId) => DriveApp.getFolderById(folderId).createFile(folder, data);
But that way I get the message folderName is undefined.
Based on the suggestion made in the The WizEd answer's comments, this was my last attempt:
Modified excerpt in the .gs file:
const saveDataAsCSV = (data, folderId) => DriveApp.getFolderById(folderId).createFile(newFolderId, data);
Modified excerpt in the HTML file:
var newFolderId = "";
/**
* call to the DriveApp api. Wrapped in a promise in case I want to address timing issues between a
* createFolder and findFolderById
*/
function createOrGetFolder(folderName, parentFolderId) {
return new Promise((resolve, reject) => {
google.script.run.withSuccessHandler(response => {
console.log("createOrGetFolder response: ", response);
if (response && response.length) {
resolve(response);
}
reject(response);
}).createOrGetFolder(folderName, parentFolderId);
newFolderId = createOrGetFolder(folderName, parentFolderId);
});
}
That way I still can't get the name of the folder created or used...
Where am I going wrong?
Global variable are not persistent. What that means is when a function is executed the instance creates the global variable but releases it when the function or function chain finishes.
Here func1() calls func2() so the instance of the global variable is perserved.
However if I run func2() by itself following running func1() it is reset to blank
Run func1()
var globalVariable = "";
function func1 () {
try {
console.log("globalVariable in func1 = ["+globalVariable+"]")
globalVariable = "Hello";
func2();
console.log("globalVariable from func2 = ["+globalVariable+"]")
}
catch(err) {
console.log("Error in func1: "+err);
}
}
function func2 () {
try {
console.log("globalVariable in func2 = ["+globalVariable+"]")
globalVariable = "Good bye";
}
catch(err) {
console.log("Error in func2: "+err);
}
}
11:53:15 AM Notice Execution started
11:53:17 AM Info globalVariable in func1 = []
11:53:17 AM Info globalVariable in func2 = [Hello]
11:53:17 AM Info globalVariable from func2 = [Good bye]
11:53:16 AM Notice Execution completed
Now run func2()
11:57:38 AM Notice Execution started
11:57:39 AM Info globalVariable in func2 = []
11:57:39 AM Notice Execution completed
To perserve the value of globalVariable from one execution to the next you need to use PropertyService
You have created a web application using Google Apps Script. The client-side code calls the server side function createOrGetFolder. You want that this function returns the name of the Folder object assigned to the folder variable.
Currently the server side function createOrGetFolder on success returns the folder id (return folder.getId();).
To get the folder name you could use folder.getName() but changing the return of this function implies to make changes to the client-side code.
One option is to add a a client function to get the folder name. This could be done by calling a server side function using the folder id that currently returns createOrGetFolder. Another way is, to make that createOrGetFolder store the folder name using the Properties Service, the Cache Service, or other store to save the folder name then using a client side function retrieve this value. In both options is very likely that the changes to the html / gs files will be minimal but this will not deliver an optimal overall performance of your web application as Google Apps Script services are slow.
Another option is to change the createOrGetFolder function return but that implies investing time on studying the client side code and changing multiple lines of code, probably will be more expensive in programmer hours than the first option but might warrant that your web application will have an optimal overall permorance by keeping the calls to the Google Apps Script services at minimum.
Resources
https://developers.google.com/apps-script/guides/html/communication
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises
I am trying to upload multiple video files in azure blob storage with the help of SAS token.
As you can see in this image :- Image
By looking in the console It looks like browser is handling the file and uploading it by chunks. So I didn't implemented it in my code. Don't know if that's the right way.
Files are uploading successfully but its taking lot of time.
<div class="container">
<div class="row">
<div class="form-group">
<label for="Files"></label>
<input type="file" id="fileControl" multiple />
<br />
<span class="" id="SizeLimitSAS" style="visibility: hidden; font-size:small"></span>
<br />
<progress id="uploadProgress" class="form-control" value="0" max="100" style="height: 60px;"></progress>
<br />
</div>
<div class="form-group">
<input type="button" id="btnUpload" value="Upload files" />
</div>
<br />
<br />
<span class="" id="countOfFileUploaded" style="visibility: hidden; font-size:large"></span>
</div>
</div>
<script src="~/Scripts/jquery-3.4.1.js"></script>
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", init, false);
function init() {
document.querySelector('#fileControl').addEventListener('change', handleFileSelect, false);
sizeLimit = document.querySelector("#SizeLimitSAS");
}
function handleFileSelect(e) {
if (!e.target.files) return;
var totalSize = 0;
sizeLimit.innerHTML = "";
var files = e.target.files;
for (var i = 0; i < files.length; i++) {
var f = files[i];
totalSize += f.size;
}
console.log(files)
console.log(totalSize)
sizeLimit.innerHTML += "</br>" + niceBytes(totalSize);
SizeLimitSAS.style.visibility = "visible";
}
const units = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
function niceBytes(x) {
let l = 0, n = parseInt(x, 10) || 0;
while (n >= 1024 && ++l) {
n = n / 1024;
}
return (n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + units[l]);
}
var count = 0;
function upload(file, type, url) {
var ajaxRequest = new XMLHttpRequest();
ajaxRequest.onreadystatechange = function (aEvt) {
console.log(ajaxRequest.readyState);
if (ajaxRequest.readyState == 4)
console.log(ajaxRequest.responseText);
};
ajaxRequest.upload.onprogress = function (e) {
var percentComplete = (e.loaded / e.total) * 100;
console.log(percentComplete + "% completed");
if (percentComplete === 100) {
count++;
countOfFileUploaded.innerHTML = count + " file uploaded";
countOfFileUploaded.style.visibility = "visible";
}
uploadProgress.value = percentComplete;
};
ajaxRequest.onerror = function (jqXHR, exception, errorThrown) {
alert(jqXHR.status + "--" + exception + "--" + errorThrown);
};
ajaxRequest.open('PUT', url, true);
ajaxRequest.setRequestHeader('Content-Type', type);
ajaxRequest.setRequestHeader('x-ms-blob-type', 'BlockBlob');
ajaxRequest.send(file);
}
jQuery("#btnUpload").click(function () {
var files = fileControl.files;
for (var i = 0, file; file = files[i]; i++) {
upload(file, file.type, "https://container.blob.core.windows.net/videos/" + file.name + "?sp=racwdli&st=2023-01-18T12:51:14Z&se=2023-01-21T20:51:14Z&sv=2021-06-08&sr=c&sig=gfgkkbhbkekhbkigyyuvuuQB2XR1ynaSOQ%3D");
}
});
</script>
I tried in my environment and successfully uploaded file parallelly in Azure blob storage using browser.
You can use the below code to upload file parallely with SAS url:
Index.html
<!-- index.html -->
<!DOCTYPE html>
<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:300px; width: 593px; overflow: scroll;" />
</body>
<script type="module" src="index.js"></script>
</html>
Index.js
const { BlobServiceClient } = require("#azure/storage-blob");
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 blobSasUrl = "<blob sas url>";
const blobServiceClient = new BlobServiceClient(blobSasUrl);
const containerName = "test";
const containerClient = blobServiceClient.getContainerClient(containerName);
const uploadFiles = async () => {
try {
reportStatus("Uploading files...");
const promises = [];
for (var fileIndex = 0; fileIndex < fileInput.files.length; fileIndex++) {
const file = fileInput.files[fileIndex];
const blockBlobClient = containerClient.getBlockBlobClient(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=blockBlobClient.uploadBrowserData(file,blobUploadOptions);
promise.then((result)=>{
var progessItem = document.getElementById('progress-' + file.name);
progessItem.innerHTML += " file link"
});
promises.push(promise);
}
await Promise.all(promises);
alert("Done.");
}
catch (error) {
alert(error.message);
}
}
selectButton.addEventListener("click", () => fileInput.click());
fileInput.addEventListener("change", uploadFiles);
Console:
Browser:
Portal:
Reference:
Quickstart: Azure Blob storage library v12 - JS Browser - Azure Storage | Microsoft Learn
I want to submit a form that has some field such as firstName, LastName, Message, Email and a file attachment by Ajax.
There is a Cancel button in the form that make cancel the uploading file but just cancelling upload!
In other words, if user click on the submit button after cancelling, the form must submit but without the attachment file. Or user can select another file and then submit.
My code has two problems:
after clicking on cancel button, if I click for the second time, uploading process begins again!
Submit button does not work!
How can I solve these problems (Preferably without the use of JQuery)?
Please help me.
Javascript code:
var ContactForm = {
xhr: new XMLHttpRequest(),
aborted: false,
form: document.querySelector("#contact-form"),
attachment: document.querySelector("#Attachment"),
progressArea: document.querySelector("#progress-area")
};
var myContactForm = ContactForm;
$(document).ready(function () {
if (myContactForm.attachment) {
myContactForm.form.addEventListener("submit",
function (submitEvent) {
submitEvent.preventDefault();
//myContactForm = Object.create(ContactForm);
const files = myContactForm.attachment.files;
//const xhr = new XMLHttpRequest();
myContactForm.xhr.open("POST", "/ContactUs/ContactUsForm/");
const formData = new FormData(myContactForm.form);
myContactForm.xhr.addEventListener("load",
function () {
console.log(myContactForm.xhr.responseText);
});
const block = addProgressBlock(files[0]);
myContactForm.xhr.upload.addEventListener("progress",
function (event) {
const progressDiv = block.querySelector(".progress-bar div");
const progressSpan = block.querySelector("span");
//progress.innerHTML = "progress" + event.loaded + " bytes sent.<br />";
if (event.lengthComputable) {
const percent = ((event.loaded / event.total) * 100).toFixed(1);
progressSpan.innerHTML = percent + "%";
progressDiv.style.width = percent + "%";
//let percent = parseInt((event.loaded / event.total) * 100);
//progress.innerHTML += "progress: " + percent + "% sent.";
}
});
myContactForm.xhr.addEventListener("abort", function () {
myContactForm.xhr.onreadystatechange = null;
myContactForm.aborted = true;
myContactForm.attachment.files = null;
myContactForm.progressArea.innerHTML = null;
});
if (myContactForm.aborted) {
myContactForm.xhr = null;
myContactForm = null;
myContactForm = Object.create(ContactForm);
return false;
}
myContactForm.xhr.send(formData);
});
}
var cancelUpload = document.querySelector("#cancelUpload");
cancelUpload.addEventListener("click", function () {
myContactForm.xhr.abort();
});
});
function addProgressBlock(file) {
const html = `<label>file: ${file.name}</label>
<div class="progress-bar">
<div class="progress-bar progress-bar-striped active" style="width: 0%;"></div>
<span>0%</span>
</div>`;
const block = document.createElement("div");
block.setAttribute("class", "progress-block");
block.innerHTML = html;
myContactForm.progressArea.appendChild(block);
return block;
}
HTML file:
<form id="contact-form" asp-controller="ContactUs" asp-action="ContactUsForm" enctype="multipart/form-data" method="post">
<div class="row">
<div class="col-md-6">
<div class="form-group">
<label for="form_name">Firstname *</label>
<input asp-for="#Model.FirstName" type="text" name="FirstName" maxlength="25" required class="form-control" placeholder="Please enter your firstname">
<div class="help-block with-errors"></div>
</div>
</div>
</div>
<div class="col-md-12">
<div class="form-group">
<div id="upload-area">
<label id="btnUploadAttachment" asp-for="#Model.Attachment" class="custom-file-upload">
<i class="fa fa-cloud-upload"></i>Upload file
</label>
<input asp-for="#Model.Attachment" name="Attachment"
type="file"
class="form-control" />
<button id="cancelUpload">cancel</button>
</div>
<div id="progress-area">
</div>
</div>
</div>
<div class="col-md-12">
<input id="submitContactForm" type="submit" class="btn btn-success btn-send" value="Sendmessage">
</div>
</form>
Cancel button in form:
<button id="cancelUpload" type="button">cancel</button>
JavaScript Code:
var ContactForm = {
xhr: new XMLHttpRequest(),
aborted: false,
form: document.querySelector("#contact-form"),
attachment: document.querySelector("#Attachment"),
progressArea: document.querySelector("#progress-area")
};
var myContactForm = Object.create(ContactForm);
$(document).ready(function () {
if (myContactForm.attachment) {
myContactForm.form.addEventListener("submit",
function (submitEvent) {
submitEvent.preventDefault();
//myContactForm = Object.create(ContactForm);
const files = myContactForm.attachment.files;
//const xhr = new XMLHttpRequest();
myContactForm.xhr.open("POST", "/ContactUs/ContactUsForm/");
const formData = new FormData(myContactForm.form);
myContactForm.xhr.addEventListener("load",
function () {
if ((myContactForm.xhr.status >= 200 && myContactForm.xhr.status < 300) || myContactForm.xhr.status === 304) {
var result = JSON.parse(myContactForm.xhr.responseText);
var messageAlert = 'alert-' + result.type;
var messageText = result.message;
var alertBox = '<div class="alert ' +
messageAlert +
' alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' +
messageText +
'</div>';
if (messageAlert && messageText) {
$('#contact-form').find('.messages').html(alertBox);
$('#contact-form')[0].reset();
}
console.log(myContactForm.xhr.responseText);
} else {
console.log("Status: " + myContactForm.xhr.status);
}
});
const block = addProgressBlock(files[0]);
myContactForm.xhr.upload.addEventListener("progress",
function (event) {
if (block != null) {
const progressDiv = block.querySelector(".progress-bar div");
const progressSpan = block.querySelector("span");
//progress.innerHTML = "progress" + event.loaded + " bytes sent.<br />";
if (event.lengthComputable) {
const percent = ((event.loaded / event.total) * 100).toFixed(1);
progressSpan.innerHTML = percent + "%";
progressDiv.style.width = percent + "%";
//let percent = parseInt((event.loaded / event.total) * 100);
//progress.innerHTML += "progress: " + percent + "% sent.";
}
}
});
myContactForm.xhr.addEventListener("abort", function () {
myContactForm.xhr.onreadystatechange = null;
myContactForm.attachment.files = null;
myContactForm.attachment = null;
myContactForm.progressArea.innerHTML = null;
myContactForm.aborted = false;
myContactForm.xhr = null;
document.querySelector("#Attachment").value = null;
myContactForm = Object.create(ContactForm);
return false;
});
myContactForm.xhr.send(formData);
});
}
var cancelUpload = document.querySelector("#cancelUpload");
cancelUpload.addEventListener("click", function () {
myContactForm.xhr.abort();
});
});
function addProgressBlock(file) {
if (file != null) {
const html = `<label>file: ${file.name}</label>
<div class="progress-bar">
<div class="progress-bar progress-bar-striped active" style="width: 0%;"></div>
<span>0%</span>
</div>`;
const block = document.createElement("div");
block.setAttribute("class", "progress-block");
block.innerHTML = html;
myContactForm.progressArea.appendChild(block);
return block;
}
return null;
}
Oddly enough if I use this code in a jsfiddle it works perfectly
var file = document.getElementById("file");
function CallAlert(){
alert(file.files[0].name);
}
<form method="post" enctype="multipart/form-data">
<div>
<label for="file">Choose file to upload</label>
<input type="file" id="file" name="file" onchange="CallAlert()">
</div>
<div>
<button>Submit</button>
</div>
</form>
The result of this is an alert with the name of the file
Now on to my issue using this same method in sorts in my case this returns Uncaught TypeError: Cannot read property '0' of undefined
function _(el) {
return document.getElementById(el);
}
function uploadFile() {
var file = _('file1').files[0];
if (typeof file === 'undefined') {
_('status').innerHTML = 'ERROR: Please browse for a file before clicking the upload button';
_('progressBar').value = 0;
} else {
$.get('https://outsource.technologyforthefuture.org/wp-content/plugins/video-contest/shortcodes/handles/upload_handle.php?getsize', function(sizelimit) {
if (sizelimit > file.size) {
var formdata = new FormData();
formdata.append('file1', file);
formdata.append('size', file.size);
var ajax = new XMLHttpRequest();
ajax.upload.addEventListener('progress', progressHandler, false);
ajax.addEventListener('load', completeHandler, false);
ajax.addEventListener('error', errorHandler, false);
ajax.addEventListener('abort', abortHandler, false);
ajax.open('POST', 'https://outsource.technologyforthefuture.org/wp-content/plugins/video-contest/shortcodes/handles/upload_handle.php');
ajax.send(formdata);
} else {
var sizewarn = 'ERROR: The File is too big! The maximum file size is ';
sizewarn += sizelimit / (1024 * 1024);
sizewarn += 'MB';
_('status').innerHTML = sizewarn;
_('progressBar').value = 0;
}
});
}
}
function progressHandler(event) {
// _('loaded_n_total_bytes').innerHTML = event.loaded+'bytes/''+event.total+'bytes';
// _('loaded_n_total_kb').innerHTML = event.loaded/1024+'kb/''+event.total/1024+'kb';
_('loaded_n_total_mb').innerHTML = Math.round(event.loaded / 1024 / 1024) + 'mb/' + Math.round(event.total / 1024 / 1024) + 'mb';
var percent = (event.loaded / event.total) * 100;
_('progressBar').value = Math.round(percent);
_('percentage_loaded').innerHTML = Math.round(percent) + '%';
if (Math.round(percent) == 100) {
_('status').innerHTML = 'Generating Link Please Wait...';
} else {
_('status').innerHTML = 'uploading... please wait';
}
}
function completeHandler(event) {
_('status').innerHTML = event.target.responseText;
_('progressBar').value = 0;
}
function errorHandler(event) {
_('status').innerHTML = 'Upload Failed';
}
function abortHandler(event) {
_('status').innerHTML = 'Upload Aborted';
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="upload_form" enctype="multipart/form-data" method="post">
<input type="file" name="file1" id="file1" onchange="uploadFile()"><br>
<progress id="progressBar" value="0" max="100" style="width:300px;"></progress>
<p class="loading">
<pt id="percentage_loaded"></pt>|
<!--<pt id="loaded_n_total_bytes"></pt>|
<pt id="loaded_n_total_kb"></pt>|-->
<pt id="loaded_n_total_mb"></pt>|
<pt id="status"></pt>
</p>
</form>
What I am trying to do is when a file is selected it sends it as an ajax response to a script to put the file on our server. I dont see how this is producing an error when there is no difference in the example snippet vs my actual code other then the extra stuff around it but I dont see how that could be affecting it.
Perhaps someone smarter then me knows what the answer to this issue is.
instead of this
function uploadFile() {
var file = _('file1').files[0];
...
try this
function uploadFile(event){
var file=event.target.files[0];
...
and don't forget to change this
<input type="file" name="file1" id="file1" onchange="(event)=>uploadFile(event)"><br>
How do you update multiple progress bars when posting a form via ajax? Here is the code I have but I can't figure it out:
Form code:
<form id="upload-form" class="no-padding" method="post" enctype="multipart/form-data">
<p><label for="folder">Create folder:</label><input type="text" name="folder" placeholder="Enter a folder name"></p>
<p><label for="file">Create file:</label><input type="text" name="file" placeholder="Enter a file name with extension (e.g. home.php)"></p>
<p class="no-margin">Upload file(s):</p>
<div class="custom-upload">
<input id="upload" type="file" name="upload[]" multiple>
<div class="fake-file">
<a class="browse text-center"><i class="fa fa-list"></i> Browse</a><input placeholder="No file(s) selected..." disabled="disabled" >
</div>
</div>
<div id="selectedFiles" class='selectedFiles'></div>
<?php echo "<input name='url' hidden value='" . $_SERVER['REQUEST_URI'] ."'>";?>
<button id="submit" name="submit"><i class="fa fa-upload"></i> Upload</button>
<p id="uploading" class='success text-right' hidden>Please be patient while your files are uploading.</p>
</form>
Javascript code:
var selDiv = "";
document.addEventListener("DOMContentLoaded", init, false);
function init() {
document.querySelector('#upload').addEventListener('change', handleFileSelect, false);
selDiv = document.querySelector("#selectedFiles");
}
var files, filesToUpload;
// populates files into array (filesToUpload) and displays selected files to the user
function handleFileSelect(e) {
if(!e.target.files) return;
selDiv.innerHTML = "";
var files = e.target.files;
filesToUpload = Array.prototype.slice.call(files);
if (files.length > 0) {
selDiv.innerHTML += '<p id="file-upload-paragraph" class="no-padding no-margin">Files selected for upload. Click the <b>x</b> to cancel file upload for a specific file:</p>';
}
for(var i = 0; i < files.length; i++) {
var f = files[i];
selDiv.innerHTML += '<div class="selectedFiles"><i class="fa fa-remove"></i><progress id="progress' + i + '" class="text-right" value="0" hidden></progress><span class="file-holder">' + f.name + ' <i class="fa fa-file"></i></span></div>';
}
}
// removes user selected file before upload
$(document).on('click', '.cancel', function () {
filesToUpload.splice($(".cancel").index(this), 1);
$(this).closest('div').remove();
if (filesToUpload.length == 0) {
$('#file-upload-paragraph').remove();
$('.custom-upload input[type=file]').val('');
}
$('.custom-upload input[type=file]').next().find('input').val(filesToUpload.length + ' files selected!');
});
// sets progress bar for each loaded file
$('#upload-form').submit(function(e){
e.preventDefault();
var url = location.href;
$("#upload").remove();
$(".cancel").hide();
$("progress").show();
var data = new FormData($('form')[0]);
if (filesToUpload.length != 0) {
for (var i = 0, j = filesToUpload.length; i < j; i++) {
data.append("upload[]", filesToUpload[i]);
}
}
$.ajax({
url: url,
type: 'POST',
data: data,
cache: false,
contentType: false,
processData: false,
xhr: function(progress) {
for (var i = 0, j = filesToUpload.length; i < j; i++) {
var progressBar = 'progress' + i;
if(document.getElementById(progressBar) == null) {
j++;
continue;
}
var xhr = new XMLHttpRequest();
(function(progressBar) {
xhr.upload.onprogress = function(e) {
$('#' + progressBar).attr({value: e.loaded, max: e.total});
};
}(progressBar));
}
return xhr;
},
success: function(res){
if(!res.error) location.reload(true);
}
});
});
PHP code:
// function call
uploadFiles(count($_FILES["upload"]["name"]), $_FILES["upload"]["tmp_name"], $_FILES["upload"]["name"], $path);
// function that uploads selected files
function uploadFiles($total, $tmpFiles, $uploadedFiles, $path) {
for($i=0; $i < $total; $i++) {
$tmpFilePath = $tmpFiles[$i];
if ($tmpFilePath != ""){
$newFilePath = "$path/" . $uploadedFiles[$i];
if(file_exists($newFilePath)) {
unlink($newFilePath);
}
move_uploaded_file($tmpFilePath, $newFilePath);
}
}
}
Here is a picture of the form, just in case:
Form image
Thanks in advance for any help.
I don't know if anyone will be looking to do the same thing but I found my own answer. Basically, replace my initial code for catching when the form is submitted with the following:
// sets progress bar for each loaded file
$('#upload-form').submit(function(e){
e.preventDefault(); // removes the default behavior of the form button
var url = location.href; // returns the current location
$("#upload").remove(); // removes the file upload box which is replaced with an array in my other code
$(".cancel").hide(); // hides the cancel buttons
$("progress").show(); // shows the hidden progress bars
// checks to see if files were selected for being uploaded
if (filesToUpload.length != 0) {
var progressBars = [];
// this loop accounts for files that were deleted after selection
for (var i = 0, j = filesToUpload.length; i < j; i++) {
if(document.getElementById('progress' + i) == null) {
j++;
continue;
}
progressBars.push(i);
}
// call to postNext function
postNext(0, progressBars, url);
// executes when only the other form elements are submitted with no file upload
} else {
var data = new FormData($('form')[0]);
$.ajax({
url: url,
type: 'POST',
data: data,
cache: false,
contentType: false,
processData: false,
success: function(res){
if(!res.error) location.reload(true);
}
});
}
});
// posts each file separately
function postNext(i, progressBars, url) {
// continues as long as there are more files to display progress bars
if (i < progressBars.length) {
var index = progressBars[i];
var data = new FormData($('form')[0]);
// after first ajax send, resets form so only the remaining file uploads are resubmitted
if (i == 0) {
$("#upload-form")[0].reset();
}
data.append("upload[]", filesToUpload[i]); //append the next file
$.ajax({
url: url, // url for post
type: 'POST',
data: data,
cache: false,
contentType: false,
processData: false,
xhr: function(progress) {
// set the progress for a given progress bar
var xhr = new XMLHttpRequest();
var progressBar = 'progress' + index;
(function(progressBar) {
xhr.upload.onprogress = function(e) {
$('#' + progressBar).attr({value: e.loaded, max: e.total});
};
}(progressBar));
return xhr;
},
beforeSend: postNext(i + 1, progressBars, url) // begins next progress bar
});
}
}
// refreshes the page only after all ajax requests are completed
$(document).bind("ajaxSend", function () {
console.log("waiting for all requests to complete...");
}).bind("ajaxStop", function () {
location.reload(true);
});