### Initialize File Upload Variables and Configuration Source: https://github.com/yohann0617/tg-disk/blob/master/static/upload.html Sets up initial variables for file handling and fetches configuration for chunk size and concurrency from the server. Uses default values if server configuration fails. ```javascript const dropZone = document.getElementById("drop-zone"); const fileInput = document.getElementById("file-input"); const fileList = document.getElementById("file-list"); let selectedFiles = []; let fileIdCounter = 0; // Default values, will be overridden by server config let CHUNK_SIZE = 20 * 1024 * 1024; // 20MB per chunk let CONCURRENT_UPLOADS = 4; // Number of concurrent chunk uploads let CONCURRENT_FILES = 3; // Number of concurrent file uploads // Fetch config from server (async function loadConfig() { try { const response = await fetch("/config"); if (response.ok) { const config = await response.json(); CHUNK_SIZE = config.chunk_size_mb * 1024 * 1024; CONCURRENT_UPLOADS = config.chunk_concurrent; CONCURRENT_FILES = config.files_concurrent; console.log("Server config loaded:", config); } } catch (e) { console.warn("Failed to load server config, using defaults:", e); } })(); ``` -------------------------------- ### Initiate File Upload Process Source: https://github.com/yohann0617/tg-disk/blob/master/static/upload.html Manages the overall upload process, including disabling the upload button, setting up upload responses, and handling concurrent file uploads using an UploadQueue. ```javascript let uploadResponses = []; let filesUploaded = 0; async function uploadFiles() { const pwd = sessionStorage.getItem("pwd"); const uploadBtn = document.getElementById("upload-btn"); uploadBtn.disabled = true; uploadBtn.innerHTML = "⏳ 上传中..."; uploadResponses = []; filesUploaded = 0; // Concurrent file upload const fileQueue = new UploadQueue(CONCURRENT_FILES); const uploadTasks = []; for (let index = 0; index < selectedFiles.length; index++) { const fileItem = selectedFiles[index]; // Skip files that are already uploaded or uploading if (fileItem.uploaded || fileItem.uploading) { continue; } fileItem.uploading = true; const task = fileQueue.add(async () => { try { const result = await uploadSingleFile(fileItem.file, fileItem.id, pwd); uploadResponses.push(result); fileItem.uploaded = true; } catch (e) { alert(`文件 ${fileItem.file.name} 上传失败: ${e.message}`); fileItem.uploading = false; } filesUploaded++; }); uploadTasks.push(task); } await Promise.all(uploadTasks); uploadBtn.disabled = false; uploadBtn.innerHTML = "🚀 开始上传"; showResultModal(uploadResponses); } ``` -------------------------------- ### Upload File in Chunks Source: https://github.com/yohann0617/tg-disk/blob/master/static/upload.html Handles the upload of a file by dividing it into chunks and uploading them concurrently. It updates a progress bar and status text during the upload process. Requires `UploadQueue`, `CHUNK_SIZE`, `CONCURRENT_UPLOADS`, `progressBar`, and `statusEl` to be defined. ```javascript async function uploadFile(file, pwd) { const CHUNK_SIZE = 1024 * 1024 * 5; // 5MB chunks const CONCURRENT_UPLOADS = 3; const totalChunks = Math.ceil(file.size / CHUNK_SIZE); console.log(` Uploading ${file.name} (${totalChunks} chunks, concurrency: ${CONCURRENT_UPLOADS})... `); const chunkIds = new Array(totalChunks); let uploadedChunks = 0; const queue = new UploadQueue(CONCURRENT_UPLOADS); const uploadTasks = []; for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) { const task = queue.add(async () => { const start = chunkIndex * CHUNK_SIZE; const end = Math.min(start + CHUNK_SIZE, file.size); const chunk = file.slice(start, end); const formData = new FormData(); formData.append("pwd", pwd); formData.append("chunk", chunk); formData.append("chunk_index", chunkIndex); formData.append("total_chunks", totalChunks); formData.append("filename", file.name); const response = await fetch("/upload_chunk", { method: "POST", body: formData }); if (!response.ok) { throw new Error(`分片 ${chunkIndex + 1} 上传失败: ${await response.text()}`); } const data = await response.json(); chunkIds[chunkIndex] = data.file_id; uploadedChunks++; const percent = (uploadedChunks / totalChunks) * 100; progressBar.style.width = percent + "%" statusEl.textContent = `上传分片 ${uploadedChunks}/${totalChunks}...`; }); uploadTasks.push(task); } await Promise.all(uploadTasks); statusEl.textContent = "合并分片..."; const mergeFormData = new FormData(); mergeFormData.append("pwd", pwd); mergeFormData.append("filename", file.name); mergeFormData.append("chunk_ids", JSON.stringify(chunkIds)); const mergeResponse = await fetch("/merge_chunks", { method: "POST", body: mergeFormData }); if (!mergeResponse.ok) { throw new Error("合并失败: " + await mergeResponse.text()); } statusEl.textContent = "✅ 完成"; return await mergeResponse.json(); } ``` -------------------------------- ### Upload Single File (Small vs. Large) Source: https://github.com/yohann0617/tg-disk/blob/master/static/upload.html Handles the upload of a single file. For small files (<= CHUNK_SIZE), it uploads directly. For large files, it initiates a concurrent chunk upload process. ```javascript async function uploadSingleFile(file, fileId, pwd) { const totalChunks = Math.ceil(file.size / CHUNK_SIZE); const statusEl = document.getElementById(`status-${fileId}`); const progressBar = document.getElementById(`bar-${fileId}`); // Small file: upload directly if (file.size <= CHUNK_SIZE) { statusEl.textContent = "上传中..."; const formData = new FormData(); formData.append("pwd", pwd); formData.append("file", file); const response = await fetch("/upload", { method: "POST", body: formData }); if (!response.ok) { throw new Error(await response.text()); } progressBar.style.width = "100%"; statusEl.textContent = "✅ 完成"; return await response.json(); } // Large file: concurrent chunk upload statusEl.textContent = `分片上传 (共 ${tot ``` -------------------------------- ### Process and Display Selected Files Source: https://github.com/yohann0617/tg-disk/blob/master/static/upload.html Adds new files to the selectedFiles array and updates the UI to display file names, sizes, and progress bars. Does not clear existing file entries. ```javascript function handleFiles(files) { // Add new files to the existing list, not replace const newFiles = Array.from(files).map(file => { const fileId = fileIdCounter++; return { id: fileId, file: file, uploading: false, uploaded: false }; }); selectedFiles = selectedFiles.concat(newFiles); // Update stats document.getElementById('stats').style.display = 'flex'; document.getElementById('file-count').textContent = selectedFiles.length; const totalBytes = selectedFiles.reduce((sum, item) => sum + item.file.size, 0); document.getElementById('total-size').textContent = formatSize(totalBytes); // Only add display items for new files, don't clear existing ones newFiles.forEach((fileItem) => { const div = document.createElement("div"); div.className = "file-item"; div.id = `file-item-${fileItem.id}`; div.innerHTML = `
📄 ${fileItem.file.name}
${formatSize(fileItem.file.size)}
等待上传...
`; fileList.appendChild(div); }); } ``` -------------------------------- ### Format File Size Source: https://github.com/yohann0617/tg-disk/blob/master/static/upload.html Converts bytes into a human-readable format (B, KB, MB, GB). ```javascript function formatSize(bytes) { if (bytes < 1024) return bytes + ' B'; if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(2) + ' KB'; if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(2) + ' MB'; return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB'; } ``` -------------------------------- ### Handle Drag and Drop Events Source: https://github.com/yohann0617/tg-disk/blob/master/static/upload.html Attaches event listeners to the drop zone for click, dragover, dragleave, and drop events. Handles file selection via click and drag-and-drop. ```javascript dropZone.addEventListener("click", () => fileInput.click()); dropZone.addEventListener("dragover", e => { e.preventDefault(); dropZone.classList.add("dragover"); }); dropZone.addEventListener("dragleave", () => dropZone.classList.remove("dragover")); dropZone.addEventListener("drop", e => { e.preventDefault(); dropZone.classList.remove("dragover"); handleFiles(e.dataTransfer.files); }); fileInput.addEventListener("change", () => handleFiles(fileInput.files)); ``` -------------------------------- ### Display Upload Results Modal Source: https://github.com/yohann0617/tg-disk/blob/master/static/upload.html Generates and displays a modal with download links for uploaded files in various formats (direct link, URL, HTML, Markdown, BBCode). It populates a container with formatted results and includes copy-to-clipboard functionality for each link type. ```javascript function showResultModal(list) { const container = document.getElementById("result-links"); container.innerHTML = ""; list.forEach((file, index) => { const html = `点击下载`; const md = `[点击下载](${file.download_url})`; const bb = `[url=${file.download_url}]点击下载[/url]`; const div = document.createElement("div"); div.className = "result-item"; div.innerHTML = `
📄 ${file.filename} 🔗 直接下载
🔗 URL 链接
🌐 HTML 代码
📝 Markdown
💬 BBCode
`; container.appendChild(div); document.getElementById("result-modal").style.display = "flex"; } ``` -------------------------------- ### Concurrent Upload Queue Manager Source: https://github.com/yohann0617/tg-disk/blob/master/static/upload.html A class to manage concurrent asynchronous tasks, ensuring that no more than a specified number of tasks run simultaneously. It uses a queue to hold tasks waiting to be executed. ```javascript class UploadQueue { constructor(concurrency) { this.concurrency = concurrency; this.running = 0; this.queue = []; } async add(task) { while (this.running >= this.concurrency) { await new Promise(resolve => this.queue.push(resolve)); } this.running++; try { return await task(); } finally { this.running--; const resolve = this.queue.shift(); if (resolve) resolve(); } } } ``` -------------------------------- ### Close Modal Source: https://github.com/yohann0617/tg-disk/blob/master/static/upload.html Hides the modal element with the ID 'result-modal'. ```javascript function closeModal() { document.getElementById("result-modal").style.display = "none"; } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.