Upload multiple files parallel in azure blob with SAS Token in javascript - javascript

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

Related

Get the value of the variable outside the function to create a file

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 remove a file that I uploaded, I got a error

When I remove a file that I uploaded, I got a error. That is js:42 Uncaught TypeError: Cannot read property 'removeChild' of null. I have to use removeChild and var for IE. Is there a good way to fix the error?
html
<form action="" enctype="multipart/form-data" class="page_form">
<label class="ui_upload upload_label" for="upload-doc">
<input type="file" name="file" id="upload-doc"
accept=".pdf,.doc,.docx,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
multiple />
<span class="btn sm label upload_btn">upload file</span>
</label>
<div class="upload_documents_wrap visually_hide">
<div class="upload_documents"> </div>
</div>
<div class="visually_hide" id="upload-file">
<div class="upload_info shadow light upload_file">
<span class="tit sm file_name"> </span>
<span class="tit sm file_size"> </span>
<button class="file_remove" type="button">Remove</button>
</div>
</div>
<button type="submit" class="btn sm">submit</button>
</form>
js
(function () {
var formElement = document.querySelector(".page_form");
var fileChooserEl = formElement.querySelector('.upload_label input[type="file"]');
var uploadDocumentsWrap = formElement.querySelector(".upload_documents_wrap");
var uploadDocuments = uploadDocumentsWrap.querySelector(".upload_documents");
var templateItemParent = document.querySelector("#upload-file");
var templateItem = templateItemParent.querySelector(".upload_file");
var uploadFiles = [];
var myFileList = [];
var onFileChooserChange = function () {
for (var i = 0; i < fileChooserEl.files.length; i++) {
var position = templateItem.cloneNode(true);
var uploadFileName = position.querySelector(".file_name");
var uploadFileSize = position.querySelector(".file_size");
var uploadFileRemove = position.querySelector(".file_remove");
var fileName = fileChooserEl.files[i].name.toLowerCase();
uploadDocumentsWrap.classList.remove("visually_hide");
uploadFileName.textContent = fileName; // file size
var suffix = "bytes";
var size = fileChooserEl.files[i].size;
if (size >= 1024 && size < 1024000) {
suffix = "KB";
size = Math.round(size / 1024 * 100) / 100;
} else if (size >= 1024000) {
suffix = "MB";
size = Math.round(size / 1024000 * 100) / 100;
}
uploadFileSize.textContent = size + suffix;
uploadFileRemove.addEventListener("click", function (evt) {
evt.preventDefault();
myFileList = myFileList.filter(function (item) {
return item.name.toLowerCase() !== uploadFileRemove.previousElementSibling.textContent;
});
console.log(myFileList);
var index = uploadFiles.indexOf(evt.target.parentNode);
uploadFileRemove.parentNode.parentNode.removeChild(uploadFileRemove.parentNode);
uploadFiles.splice(index, 1);
myFileList.splice(index, 1);
console.log(index);
if (!uploadFiles.length) {
uploadDocumentsWrap.classList.add("visually_hide");
}
});
uploadDocuments.appendChild(position);
uploadFiles.push(position);
myFileList.push(fileChooserEl.files[i]);
}
fileChooserEl.value = "";
};
console.log(uploadFiles);
var getFormData = function () {
var data = new FormData(formElement);
for (var i = 0; i < myFileList.length; i += 1) {
data.append(fileChooserEl.name, myFileList[i]);
}
return data;
};
fileChooserEl.addEventListener("change", onFileChooserChange);
})();
The error is on this line:
uploadFileRemove.parentNode.parentNode.removeChild(uploadFileRemove.parentNode);
I debugged the code and find that you removed wrong file every time when clicking the "Remove" button. It's easier and more clear to identify which file to remove using index. I edit the code like this and it works well:
...
var index = uploadFiles.indexOf(evt.target.parentNode);
//edit
var removefile = document.querySelectorAll(".upload_info")[index];
uploadDocuments.removeChild(removefile);
//uploadFileRemove.parentNode.parentNode.removeChild(uploadFileRemove.parentNode);
uploadFiles.splice(index, 1);
myFileList.splice(index, 1);
console.log(index);
...
Result:

How can cancel uploading file using JavaScript but man can submit the rest of form fields in an Ajax request

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;
}

uploading a file in chunks using html5 , javascript and PHP

Basically i have to upload file by chunks as the file is very big,i tried using this solution uploading a file in chunks using html5 but the file is corrupt because the file reconstructed is not in order.
I tried to implement the answer given in the link but i really confused how can i implement it on my php page and my html page. If you guys could give me an advice or a way of doing it, that would be great. Thank you for your time.
The code :
upload.php
<?php
$target_path = "/home/imagesdcard/www/";
$tmp_name = $_FILES['fileToUpload']['tmp_name'];
$size = $_FILES['fileToUpload']['size'];
$name = $_FILES['fileToUpload']['name'];
$target_file = $target_path . basename($name);
$complete = "test.txt";
$com = fopen("/home/imagesdcard/www/".$complete, "ab");
error_log($target_path);
// Open temp file
$out = fopen($target_file, "wb");
if ( $out ) {
// Read binary input stream and append it to temp file
$in = fopen($tmp_name, "rb");
if ( $in ) {
while ( $buff = fread( $in, 1024) ) {
fwrite($out, $buff);
fwrite($com, $buff);
}
}
fclose($in);
fclose($out);
}
fclose($com);
?>
html
<!DOCTYPE html>
<html>
<head>
<title>Upload Files using XMLHttpRequest</title>
<script type="text/javascript">
window.BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder;
function sendRequest() {
var blob = document.getElementById('fileToUpload').files[0];
const BYTES_PER_CHUNK = 1048576; // 1MB chunk sizes.
const SIZE = blob.size;
var i=0;
var start = 0;
var end = BYTES_PER_CHUNK;
while( start < SIZE ) {
var chunk = blob.slice(start, end);
uploadFile(chunk,i);
i++;
start = end;
end = start + BYTES_PER_CHUNK;
}
}
function fileSelected() {
var file = document.getElementById('fileToUpload').files[0];
if (file) {
var fileSize = 0;
if (file.size > 1024 * 1024)
fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
else
fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';
document.getElementById('fileName').innerHTML = 'Name: ' + file.name;
document.getElementById('fileSize').innerHTML = 'Size: ' + fileSize;
document.getElementById('fileType').innerHTML = 'Type: ' + file.type;
}
}
function uploadFile(blobFile,part) {
//var file = document.getElementById('fileToUpload').files[0];
var fd = new FormData();
fd.append("fileToUpload", blobFile);
var xhr = new XMLHttpRequest();
xhr.upload.addEventListener("progress", uploadProgress, false);
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("abort", uploadCanceled, false);
xhr.open("POST", "upload.php?num="+part);
xhr.onload = function(e) {
alert("loaded!");
};
xhr.send(fd);
//alert("oen over");
}
function uploadProgress(evt) {
if (evt.lengthComputable) {
var percentComplete = Math.round(evt.loaded * 100 / evt.total);
document.getElementById('progressNumber').innerHTML = percentComplete.toString() + '%';
}
else {
document.getElementById('progressNumber').innerHTML = 'unable to compute';
}
}
function uploadComplete(evt) {
/* This event is raised when the server send back a response */
alert(evt.target.responseText);
}
function uploadFailed(evt) {
alert("There was an error attempting to upload the file.");
}
function uploadCanceled(evt) {
xhr.abort();
xhr = null;
//alert("The upload has been canceled by the user or the browser dropped the connection.");
}
</script>
</head>
<body>
<form id="form1" enctype="multipart/form-data" method="post" action="upload.php">
<div class="row">
<label for="fileToUpload">Select a File to Upload</label><br />
<input type="file" name="fileToUpload" id="fileToUpload" onchange="fileSelected();"/>
<input type="button" value="cancel" onClick="uploadCanceled();"/>
</div>
<div id="fileName"></div>
<div id="fileSize"></div>
<div id="fileType"></div>
<div class="row">
<input type="button" onclick="sendRequest();" value="Upload" />
</div>
<div id="progressNumber"></div>
</form>
</body>
</html>
Your script doesn't work because js is async.
You should change your code to:
xhr.open("POST", "upload.php?num="+part, false);
and file save fine.
My solution for upload big files by chunk.
upload.php (php part)
<?php
session_start();
if($_SERVER["REQUEST_METHOD"] == "POST")
{
$chunk = $_FILES["chunk"]["tmp_name"];
$filename = $_POST['filename'];
if(!isset($_SESSION[$filename]))
{
$_SESSION[$filename] = tempnam(sys_get_temp_dir(), 'upl');
}
$tmpfile = $_SESSION[$filename];
if(isset($chunk))
{
file_put_contents($tmpfile, file_get_contents($chunk), FILE_APPEND);
}
else
{
rename($tmpfile, $filename);
}
exit();
}
?>
upload.php (html\js part)
<!DOCTYPE html>
<html>
<head>
<title>Upload Files using XMLHttpRequest</title>
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script type="text/javascript">
function sendRequest() {
// 1MB chunk sizes.
var chunk_size = 1048570;
var file = document.getElementById('file').files[0];
var filesize = file.size;
var filename = file.name;
var pos = 0;
while(pos < filesize) {
let chunk = file.slice(pos, pos+chunk_size);
pos += chunk_size;
var data = new FormData();
data.append('chunk', chunk);
data.append('filename', filename);
$.ajax({url:'upload.php',type: 'post',async:false,data: data,processData: false,contentType: false});
let percentComplete = Math.round(pos * 100 / filesize);
document.getElementById('progressNumber').innerHTML = (percentComplete > 100 ? 100 : percentComplete) + '%';
}
$.post('upload.php',{filename:filename});
}
</script>
</head>
<body>
<form>
<div class="row">
<label for="file">Select a File to Upload</label><br />
<input type="file" name="file" id="file" onchange="sendRequest();"/>
</div>
<div id="progressNumber"></div>
</form>
</body>
</html>
but this code have one bug - progress bar don't work in chrome because sync request used, work only in firefox.

File Upload Length Error (...files.length)

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

Categories

Resources