share-files-tool
Version:
A simple file sharing application.
576 lines (490 loc) • 18.5 kB
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>