Upload a Whole Directory Through an HTML Form

Browser Folder Uploading

You have to add some parameters to the input tag to support directory uploads: webkitdirectory for Webkit-based browsers (e.g., Chrome) and mozdirectory for Mozilla's browser (Firefox).

The HTML code could look like this:

<input type="file" webkitdirectory mozdirectory … />

You could have a look at https://stackoverflow.com/a/46562869 and https://stackoverflow.com/a/8218074, which are answers to similar questions.

How to Upload a Full Folder with their files

In modern Chrome, Firefox, and Edge you can set a html attribute, webkitdiretory to let the file input become a directory select window instead. And if you also use the multiple attribute after selecting the folder all contents (and contents in subfolders) will be in the .files list

<input type="file" webkitdirectory multiple>

You would then just use the same code to include all the files for upload.

Now if you want to keep the folder structure you would have to also include the webkitRelativePath which holds the relative path for that file within the folder you selected. And use that path to create the folders on the server.

for (let i = 0; i < files.length; i++) {
let file = files[i];
let fileParamName = `file${i}`;
let filePathParamName = `filepath${i}`;
formData.append(fileParamName, file);
formData.append(filePathParamName,file.webkitRelativePath);
}

And then on the server use filePathParamName to make the directory structure and move the file to it:

//Just for example
//make sure to used sanitized data in production
$folderpath = $path.dirname($_POST["filepath23"]);
$file = $path.$_POST["filepath23"];
$file_tmp = $_FILES["file23"]["tmp_name"];

//third option is for recursive folder creation (make subfolders)
mkdir($path,0644,true);
move_uploaded_file($file_tmp, $file)

For an easier method you could put all the files into a zip file within javascript and just upload the single zip file and extract on the server. Using JSZip and PHP ZipArchive class:

var zip = new JSZip();
for (let i = 0; i < files.length; i++) {
let file = files[i];
zip.file(file.webkitRelativePath, file);
}
zip.generateAsync({type:"blob"})
.then(function(content) {
formData.append("folderzip",content);
fetch(url, {
method: 'POST',
body: formData
}).then(response => {
console.log(response);
});
});

Then in php unzip the folder to where you want it:

move_uploaded_file($file_tmp, $path);
$zip = new ZipArchive();
if($zip->open($path)){
$zip->extractTo($somepath);
$zip->close();
//delete zip file
unlink($path);
}

Client side demo of file listing using webkitRelativePath: