Download file in chunks
This commit is contained in:
@@ -44,6 +44,12 @@
|
|||||||
<div class="upload-container">
|
<div class="upload-container">
|
||||||
<div class="status" id="downloadStatus">Загрузка...</div>
|
<div class="status" id="downloadStatus">Загрузка...</div>
|
||||||
<div id="downloadContent"></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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+58
-4
@@ -72,16 +72,61 @@ async function handleDownloadPage(fileUuid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fileData = data.result.data;
|
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 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 {
|
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);
|
const response = await fetch(fileData.url);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Ошибка при загрузке файла');
|
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 blobUrl = window.URL.createObjectURL(blob);
|
||||||
|
|
||||||
const link = document.createElement('a');
|
const link = document.createElement('a');
|
||||||
@@ -91,9 +136,18 @@ async function handleDownloadPage(fileUuid) {
|
|||||||
link.click();
|
link.click();
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
|
|
||||||
// Clean up the blob URL
|
// Clean up
|
||||||
window.URL.revokeObjectURL(blobUrl);
|
window.URL.revokeObjectURL(blobUrl);
|
||||||
|
downloadProgress.style.display = 'none';
|
||||||
|
downloadButton.disabled = false;
|
||||||
|
progressText.textContent = 'Загрузка завершена!';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
progressText.textContent = '';
|
||||||
|
}, 2000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
downloadProgress.style.display = 'none';
|
||||||
|
downloadButton.disabled = false;
|
||||||
// Fallback: try direct navigation
|
// Fallback: try direct navigation
|
||||||
window.location.href = fileData.url;
|
window.location.href = fileData.url;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -333,3 +333,33 @@ button:disabled {
|
|||||||
transform: translateY(-1px);
|
transform: translateY(-1px);
|
||||||
box-shadow: 0 8px 16px -4px rgba(99, 102, 241, 0.4);
|
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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user