diff --git a/index.html b/index.html index adb6917..f750f08 100644 --- a/index.html +++ b/index.html @@ -7,6 +7,7 @@

Upload Files

- - + +
+

Files to Upload

+ +

Directory Structure

+
+ + + + +
- \ No newline at end of file + diff --git a/script.js b/script.js index b9a815e..4445b91 100644 --- a/script.js +++ b/script.js @@ -1,115 +1,265 @@ document.addEventListener('DOMContentLoaded', () => { - fetchFiles(); + fetchFiles(); - document.getElementById('upload-form').addEventListener('submit', handleFileUpload); + document.getElementById('upload-form').addEventListener('submit', handleFileUpload); + document.getElementById('file-input').addEventListener('change', updateUploadList); + + document.getElementById('select-all').addEventListener('click', handleSelectAll); + document.getElementById('batch-delete').addEventListener('click', handleBatchDelete); + document.getElementById('batch-download').addEventListener('click', handleBatchDownload); + document.getElementById('batch-preview').addEventListener('click', handleBatchPreview); }); function fetchFiles() { - fetch('/files') - .then(response => response.json()) - .then(files => { - const fileList = document.getElementById('file-list'); - fileList.innerHTML = ''; - files.forEach(file => { - const li = document.createElement('li'); - li.textContent = `${file.name} (Size: ${file.size} bytes, Date: ${new Date(file.date).toLocaleString()})`; - - const downloadButton = document.createElement('button'); - downloadButton.textContent = 'Download'; - downloadButton.onclick = () => { - window.location.href = `/download/${file.name}`; - }; - - const deleteButton = document.createElement('button'); - deleteButton.textContent = 'Delete'; - deleteButton.onclick = () => { - fetch(`/delete/${file.name}`, { method: 'DELETE' }) - .then(response => { - if (response.ok) { - li.remove(); - alert('File deleted successfully!'); - } else { - alert('Error deleting file'); - } - }); - }; - - const previewButton = document.createElement('button'); - previewButton.textContent = 'Preview'; - previewButton.onclick = () => { - showPreview(file); - }; - - li.appendChild(downloadButton); - li.appendChild(deleteButton); - li.appendChild(previewButton); - fileList.appendChild(li); - }); - }) - .catch(error => { - console.error('Error fetching file list:', error); - }); + fetch('/files') + .then(response => response.json()) + .then(files => { + const fileList = document.getElementById('file-list'); + fileList.innerHTML = ''; + files.forEach(file => { + const li = document.createElement('li'); + + const checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.value = file.name; + checkbox.dataset.path = `/uploads/${file.name}`; + const extension = file.name.split('.').pop().toLowerCase(); + checkbox.dataset.fileType = extension; + + const fileInfo = document.createElement('span'); + fileInfo.className = 'file-info'; + fileInfo.textContent = `${file.name} (Size: ${file.size} bytes, Date: ${new Date(file.date).toLocaleString()})`; + + li.appendChild(checkbox); + li.appendChild(fileInfo); + fileList.appendChild(li); + }); + }) + .catch(error => { + console.error('Error fetching file list:', error); + }); +} + +function updateUploadList(event) { + const fileInput = event.target; + const uploadList = document.getElementById('upload-list'); + uploadList.innerHTML = ''; + + if (fileInput.files.length === 0) return; + + for (const file of fileInput.files) { + const li = document.createElement('li'); + li.className = 'upload-item'; + li.innerHTML = ` + ${file.name} (Size: ${Math.round(file.size / 1024)} KB) +
+
+
+ `; + uploadList.appendChild(li); + } } function handleFileUpload(event) { - event.preventDefault(); - - const form = event.target; - const formData = new FormData(form); - - fetch('/upload', { - method: 'POST', - body: formData - }) - .then(response => response.text()) - .then(message => { - alert(message); - form.reset(); - fetchFiles(); - }) - .catch(error => { - console.error('Error uploading file:', error); - alert('File upload failed!'); - }); + event.preventDefault(); + + const form = event.target; + const formData = new FormData(form); + const fileInput = document.getElementById('file-input'); + + if (fileInput.files.length === 0) { + alert('Please select files to upload.'); + return; + } + + const xhr = new XMLHttpRequest(); + + xhr.upload.addEventListener('progress', (e) => { + if (e.lengthComputable) { + const percentComplete = (e.loaded / e.total) * 100; + const progressBars = document.querySelectorAll('.progress-bar-fill'); + + progressBars.forEach(bar => { + bar.style.width = percentComplete.toFixed(2) + '%'; + }); + } + }, false); + + xhr.addEventListener('load', () => { + if (xhr.status === 200) { + alert(xhr.responseText); + form.reset(); + document.getElementById('upload-list').innerHTML = ''; + fetchFiles(); + } else { + alert('File upload failed! Server responded with status ' + xhr.status); + } + }); + + xhr.addEventListener('error', () => { + console.error('Error uploading file:', xhr.statusText); + alert('File upload failed!'); + }); + + xhr.open('POST', '/upload'); + xhr.send(formData); +} + +function getSelectedFiles() { + const selectedCheckboxes = document.querySelectorAll('#file-list input[type="checkbox"]:checked'); + return Array.from(selectedCheckboxes).map(checkbox => ({ + name: checkbox.value, + path: checkbox.dataset.path, + type: checkbox.dataset.fileType + })); } -function showPreview(file) { - const previewContainer = document.createElement('div'); - previewContainer.className = 'preview-modal'; - - const closeButton = document.createElement('button'); - closeButton.textContent = 'Close'; - closeButton.onclick = () => { - document.body.removeChild(previewContainer); - }; - - const content = document.createElement('div'); - content.className = 'preview-content'; - - if (file.name.endsWith('.txt')) { - const textPreview = document.createElement('iframe'); - textPreview.src = file.path; - textPreview.style.width = '100%'; - textPreview.style.height = '200px'; - content.appendChild(textPreview); - } else if (file.name.match(/\.(jpg|jpeg|png|gif)$/)) { - const imgPreview = document.createElement('img'); - imgPreview.src = file.path; - imgPreview.onload = () => { - document.body.appendChild(previewContainer); - }; - imgPreview.onerror = () => { - alert('Error loading image preview'); - document.body.removeChild(previewContainer); - }; - content.appendChild(imgPreview); - } else { - content.textContent = 'Preview not available for this file type.'; - } - - previewContainer.appendChild(closeButton); - previewContainer.appendChild(content); - if (!file.name.match(/\.(jpg|jpeg|png|gif)$/)) { - document.body.appendChild(previewContainer); - } +function handleSelectAll() { + const checkboxes = document.querySelectorAll('#file-list input[type="checkbox"]'); + const allChecked = Array.from(checkboxes).every(cb => cb.checked); + + checkboxes.forEach(cb => { + cb.checked = !allChecked; + }); +} + +function handleBatchDelete() { + const filesToDelete = getSelectedFiles(); + if (filesToDelete.length === 0) { + alert('No files selected for deletion.'); + return; + } + + if (!confirm(`Are you sure you want to delete ${filesToDelete.length} file(s)?`)) { + return; + } + + const deletePromises = filesToDelete.map(file => + fetch(`/delete/${file.name}`, { method: 'DELETE' }) + .then(response => { + if (response.ok) { + return { name: file.name, status: 'Success' }; + } else { + return { name: file.name, status: 'Failed' }; + } + }) + ); + + Promise.all(deletePromises) + .then(results => { + const successfulDeletes = results.filter(r => r.status === 'Success'); + const failedDeletes = results.filter(r => r.status === 'Failed'); + + if (successfulDeletes.length > 0) { + alert(`Successfully deleted ${successfulDeletes.length} file(s).`); + } + if (failedDeletes.length > 0) { + alert(`Failed to delete ${failedDeletes.length} file(s). Check console for errors.`); + } + fetchFiles(); + }) + .catch(error => { + console.error('Error during batch deletion:', error); + alert('An error occurred during batch deletion.'); + }); +} + +function handleBatchDownload() { + const filesToDownload = getSelectedFiles(); + if (filesToDownload.length === 0) { + alert('No files selected for download.'); + return; + } + + filesToDownload.forEach(file => { + window.location.href = `/download/${file.name}`; + }); + + alert(`Initiating download for ${filesToDownload.length} file(s). Check your browser's download manager.`); +} + +function handleBatchPreview() { + const selectedFiles = getSelectedFiles(); + const previewableFiles = selectedFiles.filter(file => + file.type === 'txt' || file.type.match(/^(jpg|jpeg|png|gif)$/) + ); + + if (previewableFiles.length === 0) { + alert('No previewable files (.txt, .jpg, .png, .gif) selected.'); + return; + } + + showMultiPreview(previewableFiles); +} + +function showMultiPreview(files) { + let currentIndex = 0; + const previewContainer = document.createElement('div'); + previewContainer.className = 'preview-modal'; + + const content = document.createElement('div'); + content.className = 'preview-content'; + + const controls = document.createElement('div'); + controls.className = 'preview-controls'; + + const prevButton = document.createElement('button'); + prevButton.textContent = 'Previous'; + prevButton.onclick = () => { + if (currentIndex > 0) { + currentIndex--; + updatePreviewContent(); + } + }; + + const nextButton = document.createElement('button'); + nextButton.textContent = 'Next'; + nextButton.onclick = () => { + if (currentIndex < files.length - 1) { + currentIndex++; + updatePreviewContent(); + } + }; + + const closeButton = document.createElement('button'); + closeButton.textContent = 'Close Preview'; + closeButton.onclick = () => { + document.body.removeChild(previewContainer); + }; + + controls.appendChild(prevButton); + controls.appendChild(closeButton); + controls.appendChild(nextButton); + + const updatePreviewContent = () => { + content.innerHTML = ''; + const file = files[currentIndex]; + + prevButton.disabled = currentIndex === 0; + nextButton.disabled = currentIndex === files.length - 1; + + const counter = document.createElement('p'); + counter.textContent = `File ${currentIndex + 1} of ${files.length}: ${file.name}`; + content.appendChild(counter); + + if (file.type === 'txt') { + const textPreview = document.createElement('iframe'); + textPreview.src = file.path; + textPreview.style.width = '100%'; + textPreview.style.height = '400px'; + content.appendChild(textPreview); + } else if (file.type.match(/^(jpg|jpeg|png|gif)$/)) { + const imgPreview = document.createElement('img'); + imgPreview.src = file.path; + content.appendChild(imgPreview); + } else { + content.textContent = `Preview not available for this file type: ${file.name}.`; + } + }; + + previewContainer.appendChild(controls); + previewContainer.appendChild(content); + document.body.appendChild(previewContainer); + + updatePreviewContent(); } diff --git a/server.js b/server.js index d055bad..eb9aaad 100644 --- a/server.js +++ b/server.js @@ -5,7 +5,6 @@ const fs = require('fs'); const app = express(); -// configure storage for uploaded files const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, 'uploads/'); @@ -18,10 +17,8 @@ const storage = multer.diskStorage({ const upload = multer({ storage: storage }); -// serve static files app.use(express.static(__dirname)); -// upload files app.post('/upload', upload.array('files'), (req, res) => { if (!req.files || req.files.length === 0) { return res.status(400).send('No files were uploaded.'); @@ -29,7 +26,6 @@ app.post('/upload', upload.array('files'), (req, res) => { res.send(`Successfully uploaded ${req.files.length} file(s)!`); }); -// get list of uploaded files app.get('/files', (req, res) => { const directoryPath = path.join(__dirname, 'uploads'); fs.readdir(directoryPath, (err, files) => { @@ -59,7 +55,6 @@ app.get('/files', (req, res) => { }); }); -// serve a specific file app.get('/uploads/:filename', (req, res) => { const filePath = path.join(__dirname, 'uploads', req.params.filename); res.sendFile(filePath, err => { @@ -69,7 +64,6 @@ app.get('/uploads/:filename', (req, res) => { }); }); -// download a specific file app.get('/download/:filename', (req, res) => { const filePath = path.join(__dirname, 'uploads', req.params.filename); res.download(filePath, err => { @@ -79,7 +73,6 @@ app.get('/download/:filename', (req, res) => { }); }); -// delete a specific file app.delete('/delete/:filename', (req, res) => { const filePath = path.join(__dirname, 'uploads', req.params.filename); fs.unlink(filePath, err => { @@ -90,7 +83,6 @@ app.delete('/delete/:filename', (req, res) => { }); }); -// start the server app.listen(3000, () => { - console.log('Server is running on http://localhost:3000'); -}); \ No newline at end of file + console.log('[*] Server is running on http://localhost:3000'); +});