Update.
This commit is contained in:
parent
c6fb77c89d
commit
9e9907bc00
116
retoors/static/js/components/upload.js
Normal file
116
retoors/static/js/components/upload.js
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
export function showUploadModal() {
|
||||||
|
document.getElementById('upload-modal').style.display = 'block';
|
||||||
|
// Clear previous selections and progress
|
||||||
|
document.getElementById('selected-files-preview').innerHTML = '';
|
||||||
|
document.getElementById('upload-progress-container').innerHTML = '';
|
||||||
|
document.getElementById('file-input-multiple').value = ''; // Clear selected files
|
||||||
|
document.getElementById('start-upload-btn').disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const fileInput = document.getElementById('file-input-multiple');
|
||||||
|
const selectedFilesPreview = document.getElementById('selected-files-preview');
|
||||||
|
const startUploadBtn = document.getElementById('start-upload-btn');
|
||||||
|
const uploadProgressContainer = document.getElementById('upload-progress-container');
|
||||||
|
|
||||||
|
let filesToUpload = [];
|
||||||
|
|
||||||
|
fileInput.addEventListener('change', (event) => {
|
||||||
|
filesToUpload = Array.from(event.target.files);
|
||||||
|
selectedFilesPreview.innerHTML = ''; // Clear previous previews
|
||||||
|
uploadProgressContainer.innerHTML = ''; // Clear previous progress bars
|
||||||
|
|
||||||
|
if (filesToUpload.length > 0) {
|
||||||
|
startUploadBtn.disabled = false;
|
||||||
|
filesToUpload.forEach(file => {
|
||||||
|
const fileEntry = document.createElement('div');
|
||||||
|
fileEntry.className = 'file-entry';
|
||||||
|
fileEntry.innerHTML = `
|
||||||
|
<span class="file-name">${file.name}</span>
|
||||||
|
<span class="file-size">(${(file.size / 1024 / 1024).toFixed(2)} MB)</span>
|
||||||
|
<div class="thumbnail-preview"></div>
|
||||||
|
`;
|
||||||
|
selectedFilesPreview.appendChild(fileEntry);
|
||||||
|
|
||||||
|
// Display thumbnail for image files
|
||||||
|
if (file.type.startsWith('image/')) {
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
const img = document.createElement('img');
|
||||||
|
img.src = e.target.result;
|
||||||
|
fileEntry.querySelector('.thumbnail-preview').appendChild(img);
|
||||||
|
};
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
startUploadBtn.disabled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
startUploadBtn.addEventListener('click', () => {
|
||||||
|
if (filesToUpload.length > 0) {
|
||||||
|
uploadFiles(filesToUpload);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
async function uploadFiles(files) {
|
||||||
|
startUploadBtn.disabled = true; // Disable button during upload
|
||||||
|
uploadProgressContainer.innerHTML = ''; // Clear previous progress
|
||||||
|
|
||||||
|
const currentPath = new URLSearchParams(window.location.search).get('path') || '';
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
|
||||||
|
const progressBarContainer = document.createElement('div');
|
||||||
|
progressBarContainer.className = 'progress-bar-container';
|
||||||
|
progressBarContainer.innerHTML = `
|
||||||
|
<div class="file-name">${file.name}</div>
|
||||||
|
<div class="progress-bar-wrapper">
|
||||||
|
<div class="progress-bar" id="progress-${file.name.replace(/\./g, '-')}" style="width: 0%;"></div>
|
||||||
|
</div>
|
||||||
|
<div class="progress-text" id="progress-text-${file.name.replace(/\./g, '-')}" >0%</div>
|
||||||
|
`;
|
||||||
|
uploadProgressContainer.appendChild(progressBarContainer);
|
||||||
|
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('POST', `/files/upload?current_path=${encodeURIComponent(currentPath)}`, true);
|
||||||
|
|
||||||
|
xhr.upload.addEventListener('progress', (event) => {
|
||||||
|
if (event.lengthComputable) {
|
||||||
|
const percent = (event.loaded / event.total) * 100;
|
||||||
|
document.getElementById(`progress-${file.name.replace(/\./g, '-')}`).style.width = `${percent}%`;
|
||||||
|
document.getElementById(`progress-text-${file.name.replace(/\./g, '-')}`).textContent = `${Math.round(percent)}%`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
xhr.addEventListener('load', () => {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
console.log(`File ${file.name} uploaded successfully.`);
|
||||||
|
// Update progress to 100% on completion
|
||||||
|
document.getElementById(`progress-${file.name.replace(/\./g, '-')}`).style.width = `100%`;
|
||||||
|
document.getElementById(`progress-text-${file.name.replace(/\./g, '-')}`).textContent = `100% (Done)`;
|
||||||
|
} else {
|
||||||
|
console.error(`Error uploading ${file.name}: ${xhr.statusText}`);
|
||||||
|
document.getElementById(`progress-text-${file.name.replace(/\./g, '-')}`).textContent = `Failed (${xhr.status})`;
|
||||||
|
document.getElementById(`progress-${file.name.replace(/\./g, '-')}`).style.backgroundColor = `red`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
xhr.addEventListener('error', () => {
|
||||||
|
console.error(`Network error uploading ${file.name}.`);
|
||||||
|
document.getElementById(`progress-text-${file.name.replace(/\./g, '-')}`).textContent = `Network Error`;
|
||||||
|
document.getElementById(`progress-${file.name.replace(/\./g, '-')}`).style.backgroundColor = `red`;
|
||||||
|
});
|
||||||
|
|
||||||
|
xhr.send(formData);
|
||||||
|
}
|
||||||
|
// After all files are sent, refresh the page to show new files
|
||||||
|
// A small delay to allow server to process and update file list
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
50
retoors/views/upload.py
Normal file
50
retoors/views/upload.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
from aiohttp import web
|
||||||
|
import aiohttp_jinja2
|
||||||
|
from aiohttp.web_response import json_response
|
||||||
|
|
||||||
|
from ..helpers.auth import login_required
|
||||||
|
|
||||||
|
class UploadView(web.View):
|
||||||
|
@login_required
|
||||||
|
async def post(self):
|
||||||
|
user_email = self.request["user"]["email"]
|
||||||
|
file_service = self.request.app["file_service"]
|
||||||
|
# Get current path from query parameter or form data
|
||||||
|
current_path = self.request.query.get("current_path", "")
|
||||||
|
|
||||||
|
try:
|
||||||
|
reader = await self.request.multipart()
|
||||||
|
files_uploaded = []
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
while True:
|
||||||
|
field = await reader.next()
|
||||||
|
if field is None:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Check if the field is a file input
|
||||||
|
if field.name == "file": # Assuming the input field name is 'file'
|
||||||
|
filename = field.filename
|
||||||
|
if not filename:
|
||||||
|
errors.append("Filename is required for one of the files.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
content = await field.read()
|
||||||
|
# Construct the full file path relative to the user's base directory
|
||||||
|
full_file_path_for_service = f"{current_path}/{filename}" if current_path else filename
|
||||||
|
|
||||||
|
success = await file_service.upload_file(user_email, full_file_path_for_service, content)
|
||||||
|
if success:
|
||||||
|
files_uploaded.append(filename)
|
||||||
|
else:
|
||||||
|
errors.append(f"Failed to upload file '{filename}'")
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
return json_response({"status": "error", "message": "Some files failed to upload", "details": errors}, status=500)
|
||||||
|
elif files_uploaded:
|
||||||
|
return json_response({"status": "success", "message": f"Successfully uploaded {len(files_uploaded)} files", "files": files_uploaded})
|
||||||
|
else:
|
||||||
|
return json_response({"status": "error", "message": "No files were uploaded"}, status=400)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return json_response({"status": "error", "message": f"Upload error: {str(e)}"}, status=500)
|
||||||
Loading…
Reference in New Issue
Block a user