const FILE_CHUNK_SIZE = 16384; const fileInputContainer = document.getElementById('file_input_container'); const fileInput = document.getElementById('file_input'); const fileInputMessage = document.getElementById('file_input_message'); const fileListContainer = document.getElementById('file_list_container'); const fileList = document.getElementById('file_list'); const uploadButton = document.getElementById('upload'); const downloadLinkContainer = document.getElementById('download_link_container'); const downloadLink = document.getElementById('download_link'); const progressContainer = document.getElementById('progress_container'); const progress = document.getElementById('progress'); const progressBarFilled = document.getElementById('progress_bar_filled'); let files = []; let socket = null; let fileIndex = 0; let byteIndex = 0; let bytesSent = 0; let totalBytes = 0; function sendMetadata() { const metadata = files.map((file) => ({ name: file.name, size: file.size, modtime: file.lastModified, })); socket.send(JSON.stringify(metadata)); } function finishSending() { if (socket.bufferedAmount > 0) { window.setTimeout(finishSending, 1000); return; } socket.close(); progressContainer.textContent = "Upload complete!"; } function sendData() { if (fileIndex >= files.length) { finishSending(); return; } const currentFile = files[fileIndex]; if (byteIndex < currentFile.size) { const endpoint = Math.min(byteIndex+FILE_CHUNK_SIZE, currentFile.size); const data = currentFile.slice(byteIndex, endpoint); socket.send(data); byteIndex = endpoint; bytesSent += data.size; updateProgress(); } else { fileIndex += 1; byteIndex = 0; sendData(); } } function updateProgress() { let percentage; if (totalBytes === 0) { percentage = "0%"; } else { percentage = `${(bytesSent*100/totalBytes).toFixed(1)}%`; } progress.textContent = percentage; progressBarFilled.style.width = percentage; } function updateFiles() { totalBytes = files.reduce((acc, file) => acc + file.size, 0); if (files.length === 0) { fileInputMessage.textContent = 'Select files to upload...'; fileListContainer.style.display = 'none'; uploadButton.style.display = 'none'; } else { fileInputMessage.textContent = 'Select more files to upload...'; fileListContainer.style.display = ''; uploadButton.textContent = `Upload ${files.length} file${files.length > 1 ? 's' : ''} (${displaySize(totalBytes)})`; uploadButton.style.display = ''; } } updateFiles(); downloadLinkContainer.style.display = 'none'; progressContainer.style.display = 'none'; function addFile(newFile) { if (files.some((oldFile) => newFile.name === oldFile.name)) { return; } files.push(newFile); addListEntry(newFile); } function addListEntry(file) { const listEntry = document.createElement('tr'); const deleteButtonCell = document.createElement('td'); const deleteButton = document.createElement('button'); deleteButtonCell.appendChild(deleteButton); deleteButton.className = 'file_delete'; deleteButton.textContent = 'x'; deleteButton.addEventListener('click', () => { removeFile(file.name); listEntry.remove(); updateFiles(); }); const sizeCell = document.createElement('td'); sizeCell.className = 'file_size'; sizeCell.textContent = displaySize(file.size); const nameCell = document.createElement('td'); nameCell.className = 'file_name'; nameCell.textContent = file.name; listEntry.appendChild(deleteButtonCell); listEntry.appendChild(sizeCell); listEntry.appendChild(nameCell); fileList.appendChild(listEntry); } function removeFile(name) { files = files.filter((file) => file.name !== name); } fileInput.addEventListener('input', (e) => { for (const file of e.target.files) { addFile(file); } updateFiles(); e.target.value = ''; }); uploadButton.addEventListener('click', (e) => { if (files.length === 0) { return; } fileInputContainer.remove(); for (const button of Array.from(document.getElementsByTagName('button'))) { button.remove(); } socket = new WebSocket(`${window.location.protocol === 'http:' ? 'ws' : 'wss'}://${window.location.host}/upload`); socket.addEventListener('open', sendMetadata); socket.addEventListener('message', (msg) => { if (bytesSent === 0 && msg.data.match(/^[A-Za-z0-9]+$/)) { downloadLink.textContent = `${window.location.origin}/download/${msg.data}`; downloadLinkContainer.style.display = ''; updateProgress(); progressContainer.style.display = ''; sendData(); } else if (msg.data === 'ack') { sendData(); } }); })