uploadkro / templates /index.html
triflix's picture
Create templates/index.html
788bfc4 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>File Uploader</title>
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<style>
body {
padding-top: 40px;
padding-bottom: 40px;
background-color: #f5f5f5;
}
.container {
max-width: 600px;
}
.upload-form {
background-color: #fff;
padding: 30px;
border-radius: 8px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
.progress-container {
margin-top: 20px;
display: none; /* Hidden by default */
}
#uploadInfo {
margin-top: 10px;
font-size: 0.9em;
}
#downloadLinkContainer {
margin-top: 20px;
display: none; /* Hidden by default */
}
.loader {
border: 5px solid #f3f3f3; /* Light grey */
border-top: 5px solid #3498db; /* Blue */
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
display: none; /* Hidden by default */
margin: 10px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="container">
<div class="upload-form">
<h2 class="text-center mb-4">Upload a File</h2>
<form id="uploadForm" enctype="multipart/form-data">
<div class="mb-3">
<label for="fileInput" class="form-label">Choose file</label>
<input class="form-control" type="file" id="fileInput" name="file" required>
</div>
<button type="submit" class="btn btn-primary w-100">Upload</button>
</form>
<div class="loader" id="loader"></div>
<div class="progress-container" id="progressContainer">
<div class="progress">
<div id="progressBar" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
</div>
<div id="uploadInfo" class="text-muted"></div>
</div>
<div id="downloadLinkContainer" class="alert alert-success" role="alert">
<strong>Success!</strong> File uploaded. <br>
Download link: <a href="#" id="downloadLink" target="_blank"></a>
</div>
<div id="errorContainer" class="alert alert-danger mt-3" role="alert" style="display: none;">
<strong>Error:</strong> <span id="errorMessage"></span>
</div>
</div>
</div>
<!-- Bootstrap JS Bundle (Popper.js included) -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
<script>
const uploadForm = document.getElementById('uploadForm');
const fileInput = document.getElementById('fileInput');
const loader = document.getElementById('loader');
const progressContainer = document.getElementById('progressContainer');
const progressBar = document.getElementById('progressBar');
const uploadInfo = document.getElementById('uploadInfo');
const downloadLinkContainer = document.getElementById('downloadLinkContainer');
const downloadLink = document.getElementById('downloadLink');
const errorContainer = document.getElementById('errorContainer');
const errorMessage = document.getElementById('errorMessage');
let startTime;
uploadForm.addEventListener('submit', async function(event) {
event.preventDefault();
const file = fileInput.files[0];
if (!file) {
alert('Please select a file to upload.');
return;
}
const formData = new FormData();
formData.append('file', file);
// Reset UI
loader.style.display = 'block';
progressContainer.style.display = 'block';
progressBar.style.width = '0%';
progressBar.textContent = '0%';
uploadInfo.textContent = 'Starting upload...';
downloadLinkContainer.style.display = 'none';
errorContainer.style.display = 'none';
startTime = Date.now();
const xhr = new XMLHttpRequest();
xhr.open('POST', '/upload/', true);
xhr.upload.onprogress = function(event) {
if (event.lengthComputable) {
const percentComplete = Math.round((event.loaded / event.total) * 100);
progressBar.style.width = percentComplete + '%';
progressBar.textContent = percentComplete + '%';
const elapsedTime = (Date.now() - startTime) / 1000; // seconds
const speed = event.loaded / elapsedTime; // bytes per second
const speedMbps = (speed * 8 / 1000000).toFixed(2); // Mbps
uploadInfo.textContent = `Uploaded ${formatBytes(event.loaded)} of ${formatBytes(event.total)} (${percentComplete}%) at ${speedMbps} Mbps`;
}
};
xhr.onload = function() {
loader.style.display = 'none';
if (xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
uploadInfo.textContent = 'Upload complete!';
progressBar.classList.remove('progress-bar-animated');
progressBar.classList.add('bg-success');
downloadLink.href = response.download_url;
// The filename in the link text should be the one returned by the server (sanitized)
downloadLink.textContent = response.filename;
downloadLinkContainer.style.display = 'block';
} else {
progressBar.classList.remove('progress-bar-animated');
progressBar.classList.add('bg-danger');
try {
const errorResponse = JSON.parse(xhr.responseText);
errorMessage.textContent = errorResponse.detail || `Server error: ${xhr.status}`;
} catch (e) {
errorMessage.textContent = `Server error: ${xhr.status} - ${xhr.statusText}`;
}
errorContainer.style.display = 'block';
uploadInfo.textContent = 'Upload failed.';
}
};
xhr.onerror = function() {
loader.style.display = 'none';
progressBar.classList.remove('progress-bar-animated');
progressBar.classList.add('bg-danger');
errorMessage.textContent = 'Network error or server unavailable.';
errorContainer.style.display = 'block';
uploadInfo.textContent = 'Upload failed.';
};
xhr.send(formData);
});
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
</script>
</body>
</html>