UNPKG

share-files-tool

Version:

A simple file sharing application.

576 lines (490 loc) 18.5 kB
<!DOCTYPE html> <html lang="en"> <head> <title>Files upload</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> <style> .title { margin-top: 0; } body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background-color: #f4f4f4; position: relative; } h1, h2 { color: #333; } form { background: white; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); margin-bottom: 20px; position: relative; } input[type="file"] { display: none; /* 隐藏文件输入框 */ } .file-select-container { display: flex; width: 100%; justify-content: center; align-items: center; } .file-select-container button:first-child { margin-right: 10px; } .file-select { background-color: #5cb85c; color: white; border: none; padding: 10px 15px; border-radius: 5px; cursor: pointer; font-size: 16px; width: 100%; max-width: 300px; margin-left: auto; margin-right: auto; } .btn-submit { width: 100%; max-width: 300px; } .file-select:hover { background-color: #4cae4c; } button { background-color: #5cb85c; color: white; border: none; padding: 10px 15px; border-radius: 5px; cursor: pointer; font-size: 16px; } button:hover { background-color: #4cae4c; } /* Progress bar styles */ .progress-container { width: 100%; height: 2px; background-color: #f3f3f3; border-radius: 5px; overflow: hidden; margin-bottom: 0px; display: none; } .progress-bar { height: 100%; width: 0; background-color: #5cb85c; transition: width 0.3s; } ul { list-style-type: none; padding: 0; } li { margin: 10px 0; display: flex; justify-content: space-between; align-items: center; } a { text-decoration: none; color: #007bff; font-weight: bold; font-size: 1.5rem; } a:hover { text-decoration: underline; } .view-files { margin-top: 1rem; display: flex; flex-direction: column; justify-content: center; align-items: center; } .qrcode-container { text-align: center; bottom: 0; left: 0; width: 100%; margin-top: 30px; } .file-list { /* margin-top: 20px; */ text-align: left; background-color: #fff; /* border: 1px solid #ddd; */ border-radius: 8px; width: 100%; margin-bottom: 0.3rem; /* padding: 15px; */ /* box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); */ } .file-list h2 { margin: 0 0 10px; font-size: 1.5rem; color: #333; } .file-list ul { padding: 0; list-style-type: none; } .file-list li { background-color: #f9f9f9; border: 1px solid #e0e0e0; border-radius: 5px; padding: 10px; margin: 5px 0; transition: background-color 0.3s; } .file-list li:hover { background-color: #eaeaea; } .qrcode-container img { width: 200px; } /* Delete button style */ .delete-button { margin-left: 10px; background-color: #d9534f; color: white; border: none; padding: 5px 10px; border-radius: 5px; cursor: pointer; } .delete-button:hover { background-color: #c9302c; } /* Drag and drop area styles */ #drop-area { border: 2px dashed #ccc; border-radius: 20px; /* margin: 20px auto; */ margin-bottom: 1rem; padding: 20px; text-align: center; font-size: 1.2rem; position: relative; overflow: hidden; } #drop-area.highlight { border-color: purple; background: #f8f8f8; } #drag-text { margin-top: 0; margin-bottom: 10px; text-decoration: underline; color: #5cb85c; } #uploadForm { display: flex; flex-direction: column; justify-content: center; align-items: center; } </style> <style> .share-buttons { max-width: 400px; margin: 2rem auto; padding: 20px; background-color: #fff; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); display: flex; flex-direction: column; } .share-buttons-container { display: flex; align-items: center; justify-content: center; } .share-buttons button { background-color: #4CAF50; /* 绿色 */ color: white; border: none; padding: 10px 15px; margin: 5px 0; border-radius: 5px; cursor: pointer; transition: background-color 0.3s; width: 100%; font-size: 16px; margin-right: 1rem; } .share-buttons button:last-child { margin-right: 0; } .share-buttons button:hover { background-color: #45a049; /* 深绿色 */ } .share-buttons .last-updated { margin-top: 10px; } .share-buttons textarea { width: 100%; padding: 10px; /* margin-top: 10px; */ border: 1px solid #ccc; border-radius: 5px; resize: none; /* 禁止调整大小 */ font-size: 16px; box-sizing: border-box; } .share-buttons textarea:focus { border-color: #4CAF50; /* 聚焦时框线变为绿色 */ outline: none; /* 去掉默认的轮廓 */ } li .file-name { max-width: 100%; white-space: nowrap; /* 不換行 */ overflow: hidden; /* 隱藏超出部分 */ text-overflow: ellipsis; } </style> <script src="/socket.io/socket.io.js"></script> </head> <body> <h1 class="title">Files upload</h1> <!-- Progress Bar --> <div class="progress-container" id="progressContainer"> <div class="progress-bar" id="progressBar"></div> </div> <form id="uploadForm"> <input type="file" name="files" id="fileInput" multiple required> <div id="drop-area" onclick="document.getElementById('fileInput').click();"> <div id="drag-text">Drag files into this area or click to select files</div> </div> <div class="file-list" id="fileList"> <div>Selected Files:</div> <ul id="fileNames"></ul> </div> <div class="file-select-container"> <!-- <button type="button" class="file-select" onclick="document.getElementById('fileInput').click();">Select Files</button> --> <button type="submit" class="btn-submit">Upload</button> </div> </form> <div class="view-files"> <h2>View your files? <a href="/list-downloads">(Click here)</a></h2> </div> <div class="qrcode-container"> <h2>Access the application</h2> <div id="qrcode-container"></div> </div> <div class="share-buttons"> <div class="share-buttons-container"> <button id="shareClipboard">Share<br />Content</button> <button id="getClipboard">Get<br />Content</button> <button id="clearClipboard">Clear<br />Content</button> </div> <span class="last-updated">last updated: <span id="lastUpdated"></span></span> <textarea id="clipboardContent" rows="4" cols="50" placeholder="Paste your content here..."></textarea> </div> <script> window.addEventListener('load', function () { async function loadQRCode() { const response = await fetch('/qrcode'); const qrCodeImgSrc = await response.text(); document.getElementById('qrcode-container').innerHTML = `<img src="${qrCodeImgSrc}" alt="QR Code" />`; } loadQRCode(); var selectedDataTransfer = new DataTransfer(); selectedDataTransfer.items.clear(); function selectFiles(files) { const fileInput = document.getElementById('fileInput'); const fileNamesList = document.getElementById('fileNames'); fileNamesList.innerHTML = ''; // Clear existing file list let sourceFiles = Array.from(files); sourceFiles.forEach(f => { if (!Array.from(selectedDataTransfer.files).find(ff => ff.name == f.name)) { selectedDataTransfer.items.add(f) } }) targetFiles = selectedDataTransfer.files; fileInput.files = targetFiles let selectedFiles = Array.from(targetFiles).map(file => file.name); // Store the name of input files // Render the selected files list selectedFiles.forEach((fileName) => { const li = document.createElement('li'); const div = document.createElement('div'); div.textContent = fileName; div.className = 'file-name'; li.appendChild(div); // Create delete button const delButton = document.createElement('button'); delButton.textContent = 'Del'; // Set delete button text delButton.className = 'delete-button'; // Add delete button style class delButton.onclick = function () { const index = selectedFiles.indexOf(fileName); if (index > -1) { selectedFiles.splice(index, 1); // Remove the file from the array const file = Array.from(selectedDataTransfer.files).find(f => f.name === fileName); selectedDataTransfer.items.remove(file); // Remove the file from the data transfer object } const fileInput = document.getElementById('fileInput'); fileInput.files = selectedDataTransfer.files; // Update the input files li.remove(); // Remove the file name from the list }; li.appendChild(delButton); // Attach delete button to list item fileNamesList.appendChild(li); // Add to the list }); } function fileInputHandler() { const fileInput = document.getElementById('fileInput'); // const fileNamesList = document.getElementById('fileNames'); const progressContainer = document.getElementById('progressContainer'); const progressBar = document.getElementById('progressBar'); let selectedFiles = []; // Array to hold selected files fileInput.addEventListener('change', function () { selectFiles(fileInput.files); }); document.getElementById('uploadForm').addEventListener('submit', function (event) { event.preventDefault(); // Prevent the standard form submission if (!selectedDataTransfer.files.length) return; const formData = new FormData(this); // Create a FormData object // Show the progress bar progressContainer.style.display = 'block'; progressBar.style.width = '0%'; // Reset width const xhr = new XMLHttpRequest(); xhr.open('POST', '/upload', true); // Update progress bar during upload xhr.upload.onprogress = function (e) { if (e.lengthComputable) { const percentComplete = (e.loaded / e.total) * 100; progressBar.style.width = percentComplete + '%'; // Update progress bar width } }; // Handle the response xhr.onload = function () { if (xhr.status === 200) { setTimeout(() => { window.location.href = '/upload-success'; // Redirect on success }, 500); selectedDataTransfer.items.clear(); // Clear the data transfer object } }; xhr.send(formData); // Send the FormData }); } fileInputHandler(); const dropArea = document.getElementById('drop-area'); const fileInput = document.getElementById('fileInput'); // Prevent default drag behaviors ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { dropArea.addEventListener(eventName, preventDefaults, false); }); function preventDefaults(e) { e.preventDefault(); e.stopPropagation(); } // Highlight drop area when item is dragged over it ['dragenter', 'dragover'].forEach(eventName => { dropArea.addEventListener(eventName, highlight, false); }); ['dragleave', 'drop'].forEach(eventName => { dropArea.addEventListener(eventName, unhighlight, false); }); function highlight(e) { dropArea.classList.add('highlight'); } function unhighlight(e) { dropArea.classList.remove('highlight'); } // Handle dropped files dropArea.addEventListener('drop', handleDrop, false); function handleDrop(e) { let dt = e.dataTransfer; let files = dt.files; selectFiles(files); } document.getElementById('shareClipboard').addEventListener('click', () => { shareClipboard(); }); const shareClipboard = async () => { const content = document.getElementById('clipboardContent').value; if (!content) { alert("Please enter content to share."); return; } fetch(new Request("/shareClipboard", { method: "POST", body: JSON.stringify({ content: content || "" }), headers: { "Content-Type": "application/json", }, })).then(response => response.json()) .then(data => { if (data && data.content) { alert(data.content); } }); }; const fetctClipboard = () => { fetch('/getClipboard') .then(response => response.json()) .then(data => { document.getElementById('clipboardContent').value = data.content; document.getElementById('clipboardContent').select(); document.execCommand("copy"); document.getElementById('lastUpdated').textContent = new Date().toLocaleString(); }); }; document.getElementById('getClipboard').addEventListener('click', () => { fetctClipboard(); }); document.getElementById('clearClipboard').addEventListener('click', () => { fetch('/clearClipboard', { method: 'POST' }) .then(response => response.json()) .then(data => { document.getElementById('clipboardContent').value = ''; document.getElementById('lastUpdated').textContent = new Date().toLocaleString(); navigator.clipboard && navigator.clipboard.writeText(""); }) .catch(error => { alert("clearClipboard error: " + error.message); }); }); fetctClipboard(); }); </script> <script> window.addEventListener('load', function () { if (io) { const socket = io(); socket.on('clipboardUpdated', (data) => { document.getElementById('lastUpdated').textContent = new Date().toLocaleString(); document.getElementById('clipboardContent').value = data; }); socket.on('refreshPage', data => { location.reload(); }); } }); </script> </body> </html>