Download file in chunks

This commit is contained in:
IgorVolochay
2026-01-06 20:53:28 +03:00
parent 91451c07b8
commit 8c7fda2069
3 changed files with 94 additions and 4 deletions
+6
View File
@@ -44,6 +44,12 @@
<div class="upload-container">
<div class="status" id="downloadStatus">Загрузка...</div>
<div id="downloadContent"></div>
<div id="downloadProgress" class="download-progress" style="display: none;">
<div class="progress-bar-container">
<div class="progress-bar" id="progressBar"></div>
</div>
<p class="progress-text" id="progressText">0%</p>
</div>
</div>
</div>
</div>
+58 -4
View File
@@ -72,16 +72,61 @@ async function handleDownloadPage(fileUuid) {
}
const fileData = data.result.data;
const fileSize = parseInt(fileData.file_size);
// Create download button handler
// Create download button handler with progress tracking
const handleDownload = async () => {
const downloadButton = document.getElementById('downloadButton');
const downloadProgress = document.getElementById('downloadProgress');
const progressBar = document.getElementById('progressBar');
const progressText = document.getElementById('progressText');
// For small files (< 10MB), use direct download (browser's native download)
// For larger files, show progress bar
if (fileSize < 10 * 1024 * 1024) {
// Small file - use direct download with browser's native progress
const link = document.createElement('a');
link.href = fileData.url;
link.download = fileData.file_name;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
return;
}
// Large file - download with progress tracking
try {
// For presigned URLs, we fetch the file and create a blob URL
downloadButton.disabled = true;
downloadProgress.style.display = 'block';
progressBar.style.width = '0%';
progressText.textContent = '0%';
const response = await fetch(fileData.url);
if (!response.ok) {
throw new Error('Ошибка при загрузке файла');
}
const blob = await response.blob();
const contentLength = response.headers.get('content-length');
const total = contentLength ? parseInt(contentLength, 10) : fileSize;
let loaded = 0;
const reader = response.body.getReader();
const chunks = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
loaded += value.length;
const percent = Math.round((loaded / total) * 100);
progressBar.style.width = percent + '%';
progressText.textContent = `${percent}% (${formatBytes(loaded)} / ${formatBytes(total)})`;
}
// Combine chunks into blob
const blob = new Blob(chunks);
const blobUrl = window.URL.createObjectURL(blob);
const link = document.createElement('a');
@@ -91,9 +136,18 @@ async function handleDownloadPage(fileUuid) {
link.click();
document.body.removeChild(link);
// Clean up the blob URL
// Clean up
window.URL.revokeObjectURL(blobUrl);
downloadProgress.style.display = 'none';
downloadButton.disabled = false;
progressText.textContent = 'Загрузка завершена!';
setTimeout(() => {
progressText.textContent = '';
}, 2000);
} catch (error) {
downloadProgress.style.display = 'none';
downloadButton.disabled = false;
// Fallback: try direct navigation
window.location.href = fileData.url;
}
+30
View File
@@ -333,3 +333,33 @@ button:disabled {
transform: translateY(-1px);
box-shadow: 0 8px 16px -4px rgba(99, 102, 241, 0.4);
}
/* Download progress */
.download-progress {
margin-top: 24px;
text-align: center;
}
.progress-bar-container {
width: 100%;
height: 8px;
background: #e5e7eb;
border-radius: 4px;
overflow: hidden;
margin-bottom: 12px;
}
.progress-bar {
height: 100%;
background: linear-gradient(90deg, #6366f1, #8b5cf6);
border-radius: 4px;
transition: width 0.3s ease;
width: 0%;
}
.progress-text {
font-size: 14px;
color: #6b7280;
margin: 0;
font-weight: 500;
}