Skip to content

Commit

Permalink
Merge pull request #51 from alan16742/master
Browse files Browse the repository at this point in the history
support upload files
  • Loading branch information
vcheckzen authored Sep 16, 2024
2 parents 2242593 + 4f322cc commit 14a33df
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 21 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ Fast OneDrive Index / FODI,无需服务器的 OneDrive 快速列表程序

#### 更新

#### 2024.09.15

- 支持上传(在上传目录创建 `.upload` 文件)

##### 2019.12.23

- 进一步提升速度
Expand Down
66 changes: 55 additions & 11 deletions back-end-cf/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const PROTECTED_LAYERS = -1;
const EXPOSE_PASSWD = "";

addEventListener('scheduled', event => {
event.waitUntil(fetchAccessToken( /* event.scheduledTime */ ));
event.waitUntil(fetchAccessToken(/* event.scheduledTime */));
});

addEventListener("fetch", (event) => {
Expand Down Expand Up @@ -44,28 +44,51 @@ const PATH_AUTH_STATES = Object.freeze({
async function handleRequest(request) {
let queryString, querySplited, requestPath;
let abnormalWay = false;
const returnHeaders = {
"Access-Control-Allow-Origin": "*",
"Cache-Control": "max-age=3600",
"Content-Type": "application/json; charset=utf-8",
};
if (request.url.includes("?")) {
queryString = decodeURIComponent(request.url.split("?")[1]);
} else if (request.url.split("/").pop().includes(".")) {
queryString = decodeURIComponent("file=/" + request.url.split("://")[1].split(/\/(.+)/)[1]);
queryString = decodeURIComponent(
"file=/" + request.url.split("://")[1].split(/\/(.+)/)[1]
);
abnormalWay = true;
}
if (queryString) querySplited = queryString.split("=");
if ((querySplited && querySplited[0] === "file") || abnormalWay) {
const file = querySplited[1];
const fileName = file.split("/").pop();
if (fileName === PASSWD_FILENAME)
if (fileName.toLowerCase() === PASSWD_FILENAME.toLowerCase())
return Response.redirect(
"https://www.baidu.com/s?wd=%E6%80%8E%E6%A0%B7%E7%9B%97%E5%8F%96%E5%AF%86%E7%A0%81",
301
);
requestPath = file.replace("/" + fileName, "");
const url = await fetchFiles(requestPath, fileName);
return Response.redirect(url, 302);
} else if (querySplited && querySplited[0] === "upload") {
requestPath = querySplited[1];
const uploadAllow = await fetchFiles(requestPath, ".upload");
const fileList = await request.json();
const pwAttack = fileList["files"].some(file => file.remotePath.split("/").pop().toLowerCase() === PASSWD_FILENAME.toLowerCase());
if (uploadAllow && !pwAttack) {
const uploadLinks = await uploadFiles(fileList);
return new Response(uploadLinks, {
headers: returnHeaders,
});
}
return new Response(
JSON.stringify({ error: "Access forbidden" }),
{
status: 403,
headers: returnHeaders
}
);
} else {
const {
headers
} = request;
const { headers } = request;
const contentType = headers.get("content-type");
const body = {};
if (contentType && contentType.includes("form")) {
Expand All @@ -77,11 +100,7 @@ async function handleRequest(request) {
requestPath = Object.getOwnPropertyNames(body).length ? body["?path"] : "";
const files = await fetchFiles(requestPath, null, body.passwd);
return new Response(files, {
headers: {
"Access-Control-Allow-Origin": "*",
"Cache-Control": "max-age=3600",
"Content-Type": "application/json; charset=utf-8",
},
headers: returnHeaders,
});
}
}
Expand Down Expand Up @@ -247,3 +266,28 @@ async function fetchFiles(path, fileName, passwd, viewExposePathPassword) {
})).filter(file => file.name !== PASSWD_FILENAME)
});
}

async function uploadFiles(fileJsonList) {
let fileList = fileJsonList["files"];
const accessToken = await fetchAccessToken();
await Promise.all(
fileList.map(async (file) => {
const uri = `${OAUTH.apiUrl}:${EXPOSE_PATH}${file["remotePath"]}:/createUploadSession`;
const headers = {
Authorization: "Bearer " + accessToken,
};
const uploadUrl = await fetch(uri, {
method: "POST",
headers: headers,
})
.then((response) => {
return response.json();
})
.then((data) => {
return data["uploadUrl"];
});
file.uploadUrl = uploadUrl;
})
);
return JSON.stringify({ files: fileList });
}
62 changes: 52 additions & 10 deletions back-end-deployment-helper/src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const apiHost = "${apiHost}";
const redirectUri = "${replayURL}";
addEventListener('scheduled', event => {
event.waitUntil(fetchAccessToken( /* event.scheduledTime */ ));
event.waitUntil(fetchAccessToken(/* event.scheduledTime */));
});
addEventListener("fetch", (event) => {
Expand Down Expand Up @@ -52,6 +52,11 @@ const PATH_AUTH_STATES = Object.freeze({
async function handleRequest(request) {
let queryString, querySplited, requestPath;
let abnormalWay = false;
const returnHeaders = {
"Access-Control-Allow-Origin": "*",
"Cache-Control": "max-age=3600",
"Content-Type": "application/json; charset=utf-8",
};
if (request.url.includes("?")) {
queryString = decodeURIComponent(request.url.split("?")[1]);
} else if (request.url.split("/").pop().includes(".")) {
Expand All @@ -62,18 +67,34 @@ async function handleRequest(request) {
if ((querySplited && querySplited[0] === "file") || abnormalWay) {
const file = querySplited[1];
const fileName = file.split("/").pop();
if (fileName === PASSWD_FILENAME)
if (fileName.toLowerCase() === PASSWD_FILENAME.toLowerCase())
return Response.redirect(
"https://www.baidu.com/s?wd=%E6%80%8E%E6%A0%B7%E7%9B%97%E5%8F%96%E5%AF%86%E7%A0%81",
301
);
requestPath = file.replace("/" + fileName, "");
const url = await fetchFiles(requestPath, fileName);
return Response.redirect(url, 302);
} else if (querySplited && querySplited[0] === "upload") {
requestPath = querySplited[1];
const uploadAllow = await fetchFiles(requestPath, ".upload");
const fileList = await request.json();
const pwAttack = fileList["files"].some(file => file.remotePath.split("/").pop().toLowerCase() === PASSWD_FILENAME.toLowerCase());
if (uploadAllow && !pwAttack) {
const uploadLinks = await uploadFiles(fileList);
return new Response(uploadLinks, {
headers: returnHeaders,
});
}
return new Response(
JSON.stringify({ error: "Access forbidden" }),
{
status: 403,
headers: returnHeaders
}
);
} else {
const {
headers
} = request;
const { headers } = request;
const contentType = headers.get("content-type");
const body = {};
if (contentType && contentType.includes("form")) {
Expand All @@ -85,11 +106,7 @@ async function handleRequest(request) {
requestPath = Object.getOwnPropertyNames(body).length ? body["?path"] : "";
const files = await fetchFiles(requestPath, null, body.passwd);
return new Response(files, {
headers: {
"Access-Control-Allow-Origin": "*",
"Cache-Control": "max-age=3600",
"Content-Type": "application/json; charset=utf-8",
},
headers: returnHeaders,
});
}
}
Expand Down Expand Up @@ -254,5 +271,30 @@ async function fetchFiles(path, fileName, passwd, viewExposePathPassword) {
url: file["@microsoft.graph.downloadUrl"],
})).filter(file => file.name !== PASSWD_FILENAME)
});
}
async function uploadFiles(fileJsonList) {
let fileList = fileJsonList["files"];
const accessToken = await fetchAccessToken();
await Promise.all(
fileList.map(async (file) => {
const uri = \`\${OAUTH.apiUrl}:\${EXPOSE_PATH}\${file["remotePath"]}:/createUploadSession\`;
const headers = {
Authorization: "Bearer " + accessToken,
};
const uploadUrl = await fetch(uri, {
method: "POST",
headers: headers,
})
.then((response) => {
return response.json();
})
.then((data) => {
return data["uploadUrl"];
});
file.uploadUrl = uploadUrl;
})
);
return JSON.stringify({ files: fileList });
}`;
};
112 changes: 112 additions & 0 deletions front-end/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,8 @@
return "text";
} else if (["torrent"].contains(suffix)) {
return "torrent";
} else if (["upload"].contains(suffix)) {
return "upload";
} else if (
["mp4", "avi", "mkv", "flv", "m3u8"].contains(suffix)
) {
Expand Down Expand Up @@ -1227,6 +1229,23 @@
torrentUrl: url,
});
break;
case "upload":
let upload = document.createElement("div");
upload.style.textAlign = "center";
upload.id = "uploadDiv";
upload.innerHTML = "";
upload.innerHTML += `
<select
onchange="document.getElementById('fileInput').webkitdirectory=this.value;"
>
<option value="">上传文件</option>
<option value="1">上传文件夹</option>
</select>
<input type="file" id="fileInput" multiple="multiple" />
<button onclick="uploadFiles()">上传</button>`;
content.innerHTML = "";
content.appendChild(upload);
break;
case "video":
let video = document.createElement("div");
previewHandler.createDplayer(url, suffix, video);
Expand Down Expand Up @@ -1523,6 +1542,99 @@
}
}

function uploadFiles() {
const pageSize = 20;
const fileInput = Array.from(
document.getElementById("fileInput").files
);
const odPath = document
.querySelector(".file-name")
.innerHTML.replace("/.upload", "");
const upFileList = fileInput.map((f) => ({
remotePath: `${odPath}/${f.webkitRelativePath || f.name}`,
}));

function paginate(array, size) {
const paginatedArray = [];
for (let i = 0; i < array.length; i += size) {
paginatedArray.push(array.slice(i, i + size));
}
return paginatedArray;
}

const paginatedUpFileList = paginate(upFileList, pageSize);
function sendPage(pageIndex = 0) {
if (pageIndex >= paginatedUpFileList.length) {
return;
}

const currentPage = paginatedUpFileList[pageIndex];
sendRequest(
window.api.method,
window.api.url + "?upload=" + odPath,
JSON.stringify({ files: currentPage }),
window.api.headers,
(response) => {
sendPage(pageIndex + 1);
const uploadLinks = JSON.parse(response).files;
uploadLinks.forEach(({ remotePath, uploadUrl }) => {
const fileToUpload = fileInput.find(
(f) =>
`${odPath}/${f.webkitRelativePath || f.name}` === remotePath
);
if (fileToUpload)
uploadFileWithProgress(fileToUpload, uploadUrl);
});
}
);
}
sendPage(0);

function uploadFileWithProgress(file, uploadUrl) {
const xhr = new XMLHttpRequest();

const progressDiv = document.getElementById("uploadDiv");

const progressContainer = document.createElement("div");
progressContainer.className = "progress-container";
progressDiv.appendChild(progressContainer);

const progressText = document.createElement("div");
progressText.className = "progress-text";
progressContainer.appendChild(progressText);

xhr.upload.onprogress = function (event) {
if (event.lengthComputable) {
const percentComplete = Math.round(
(event.loaded / event.total) * 100
);
const progressBar =
"[" +
"=".repeat(percentComplete / 5) +
" ".repeat(20 - percentComplete / 5) +
"]";
progressText.textContent = `${file.name}: ${progressBar} ${percentComplete}%`;
}
};

xhr.onload = function () {
if (xhr.status === 200 || 201) {
progressText.textContent += " Upload complete!";
} else {
progressText.innerHTML += " <mark>Upload failed!</mark>";
}
};

xhr.onerror = function () {
progressText.innerHTML +=
"<mark> An error occurred during the upload.</mark>";
};

xhr.open("PUT", uploadUrl);
xhr.send(file);
}
}

document.addEventListener("DOMContentLoaded", () => {
document.title = window.GLOBAL_CONFIG.SITE_NAME;
document.querySelector(".site").textContent =
Expand Down

0 comments on commit 14a33df

Please sign in to comment.