diff --git a/README.md b/README.md
index a4e15e4..4dd35b7 100644
--- a/README.md
+++ b/README.md
@@ -12,10 +12,10 @@ Duf is a simple file server.
- Serve static files
- Download folder as zip file
- Search files
-- Upload files
+- Upload files and folders
- Delete files
- Basic authentication
-- Unzip zip file when upload
+- Upload zip file then unzip
- Easy to use with curl
## Install
diff --git a/src/assets/index.css b/src/assets/index.css
index 748e8ed..b32e7d0 100644
--- a/src/assets/index.css
+++ b/src/assets/index.css
@@ -159,8 +159,6 @@ body {
}
.uploaders {
- display: flex;
- flex-wrap: wrap;
padding: 0.5em 0;
}
diff --git a/src/assets/index.js b/src/assets/index.js
index 0a69c0e..6cbe802 100644
--- a/src/assets/index.js
+++ b/src/assets/index.js
@@ -1,26 +1,28 @@
let $tbody, $uploaders;
-let uploaderIdx = 0;
let baseDir;
class Uploader {
- idx = 0;
+ idx;
file;
+ name;
$elem;
- constructor(idx, file) {
- this.idx = idx;
+ static globalIdx = 0;
+ constructor(file, dirs) {
+ this.name = [...dirs, file.name].join("/");
+ this.idx = Uploader.globalIdx++;
this.file = file;
}
upload() {
- const { file, idx } = this;
- let url = getUrl(file.name);
+ const { file, idx, name } = this;
+ let url = getUrl(name);
if (file.name == baseDir + ".zip") {
url += "?unzip";
}
$uploaders.insertAdjacentHTML("beforeend", `
`);
this.$elem = document.getElementById(`file${idx}`);
@@ -43,15 +45,15 @@ class Uploader {
progress(event) {
const percent = (event.loaded / event.total) * 100;
- this.$elem.innerHTML = `${this.file.name} (${percent.toFixed(2)}%)`;
+ this.$elem.innerHTML = `${this.name} (${percent.toFixed(2)}%)`;
}
complete() {
- this.$elem.innerHTML = `${this.file.name}`;
+ this.$elem.innerHTML = `${this.name}`;
}
fail() {
- this.$elem.innerHTML = `${this.file.name}`;
+ this.$elem.innerHTML = `${this.name}`;
}
}
@@ -140,6 +142,44 @@ async function deletePath(index) {
}
}
+function dropzone() {
+ ["drag", "dragstart", "dragend", "dragover", "dragenter", "dragleave", "drop"].forEach(name => {
+ document.addEventListener(name, e => {
+ e.preventDefault();
+ e.stopPropagation();
+ });
+ });
+ document.addEventListener("drop", e => {
+ if (!e.dataTransfer.items[0].webkitGetAsEntry) {
+ const files = e.dataTransfer.files.filter(v => v.size > 0);
+ for (const file of files) {
+ new Uploader(file, []).upload();
+ }
+ } else {
+ const entries = [];
+ const len = e.dataTransfer.items.length;
+ for (let i = 0; i < len; i++) {
+ entries.push(e.dataTransfer.items[i].webkitGetAsEntry());
+ }
+ addFileEntries(entries, [])
+ }
+ });
+}
+
+async function addFileEntries(entries, dirs) {
+ for (const entry of entries) {
+ if (entry.isFile) {
+ entry.file(file => {
+ new Uploader(file, dirs).upload();
+ });
+ } else if (entry.isDirectory) {
+ const dirReader = entry.createReader()
+ dirReader.readEntries(entries => addFileEntries(entries, [...dirs, entry.name]));
+ }
+ }
+}
+
+
function getUrl(name) {
let url = location.href.split('?')[0];
if (!url.endsWith("/")) url += "/";
@@ -183,21 +223,24 @@ function formatSize(size) {
return Math.round(size / Math.pow(1024, i), 2) + ' ' + sizes[i];
}
-
function ready() {
$tbody = document.querySelector(".main tbody");
$uploaders = document.querySelector(".uploaders");
addBreadcrumb(DATA.breadcrumb);
- DATA.paths.forEach((file, index) => addPath(file, index));
+ if (Array.isArray(DATA.paths)) {
+ const len = DATA.paths.length;
+ for (let i = 0; i < len; i++) {
+ addPath(DATA.paths[i], i);
+ }
+ }
if (DATA.allow_upload) {
+ dropzone();
document.querySelector(".upload-control").classList.remove(["hidden"]);
document.getElementById("file").addEventListener("change", e => {
const files = e.target.files;
for (let file of files) {
- uploaderIdx += 1;
- const uploader = new Uploader(uploaderIdx, file);
- uploader.upload();
+ new Uploader(file, []).upload();
}
});
}