Fix chunked ZIP upload issues

- Fix FileLike object to have proper filename attribute for validation
- Implement parallel chunk uploading (3 concurrent chunks) to speed up uploads
- Update all templates with improved chunking logic
- Fixes validation error and slow upload performance
parent 4a984e2d
......@@ -534,8 +534,10 @@
function uploadFileInChunks(file, matchId) {
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
const uploadId = Date.now() + '_' + Math.random().toString(36).substr(2, 9);
const maxConcurrent = 3; // Upload up to 3 chunks simultaneously
let currentChunk = 0;
let uploadedChunks = 0;
let failed = false;
// Show progress bar and hide upload form
const uploadForm = document.getElementById(`upload_form_${matchId}`);
......@@ -548,48 +550,80 @@
statusDiv.className = 'upload-status uploading';
statusDiv.textContent = 'Uploading... 0%';
function uploadChunk() {
if (currentChunk >= totalChunks) {
// All chunks uploaded, finalize
finalizeUpload(uploadId, file.name, matchId);
return;
}
function uploadChunk(chunkIndex) {
if (failed) return;
const start = currentChunk * CHUNK_SIZE;
const start = chunkIndex * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('chunk', chunk);
formData.append('chunkIndex', currentChunk);
formData.append('chunkIndex', chunkIndex);
formData.append('totalChunks', totalChunks);
formData.append('uploadId', uploadId);
formData.append('fileName', file.name);
formData.append('matchId', matchId);
fetch('/upload/chunk', {
return fetch('/upload/chunk', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
currentChunk++;
const progress = (currentChunk / totalChunks) * 100;
if (!data.success) {
throw new Error(data.error || 'Upload failed');
}
uploadedChunks++;
const progress = (uploadedChunks / totalChunks) * 100;
updateProgress(matchId, Math.round(progress));
statusDiv.textContent = `Uploading... ${Math.round(progress)}%`;
uploadChunk();
});
}
// Start uploading chunks in parallel
const promises = [];
for (let i = 0; i < Math.min(maxConcurrent, totalChunks); i++) {
promises.push(uploadNextChunk(i));
}
function uploadNextChunk(startIndex) {
let currentIndex = startIndex;
return new Promise((resolve, reject) => {
function uploadNext() {
if (currentIndex >= totalChunks || failed) {
resolve();
return;
}
uploadChunk(currentIndex)
.then(() => {
currentIndex += maxConcurrent;
if (currentIndex < totalChunks) {
uploadNext();
} else {
throw new Error(data.error || 'Upload failed');
resolve();
}
})
.catch(error => {
statusDiv.className = 'upload-status error';
statusDiv.textContent = 'Upload failed: ' + error.message;
failed = true;
reject(error);
});
}
uploadNext();
});
}
uploadChunk();
Promise.all(promises)
.then(() => {
if (!failed) {
finalizeUpload(uploadId, file.name, matchId);
}
})
.catch(error => {
statusDiv.className = 'upload-status error';
statusDiv.textContent = 'Upload failed: ' + error.message;
});
}
function finalizeUpload(uploadId, fileName, matchId) {
......@@ -690,8 +724,10 @@
function uploadFixtureFileInChunks(file) {
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
const uploadId = Date.now() + '_' + Math.random().toString(36).substr(2, 9);
const maxConcurrent = 3; // Upload up to 3 chunks simultaneously
let currentChunk = 0;
let uploadedChunks = 0;
let failed = false;
// Show progress bar and hide upload form
const uploadForm = document.getElementById('fixture_upload_form');
......@@ -704,38 +740,73 @@
statusDiv.className = 'upload-status uploading';
statusDiv.textContent = 'Uploading to all matches... 0%';
function uploadChunk() {
if (currentChunk >= totalChunks) {
// All chunks uploaded, finalize
finalizeFixtureUpload(uploadId, file.name);
return;
}
function uploadChunk(chunkIndex) {
if (failed) return;
const start = currentChunk * CHUNK_SIZE;
const start = chunkIndex * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('chunk', chunk);
formData.append('chunkIndex', currentChunk);
formData.append('chunkIndex', chunkIndex);
formData.append('totalChunks', totalChunks);
formData.append('uploadId', uploadId);
formData.append('fileName', file.name);
fetch('/upload/chunk', {
return fetch('/upload/chunk', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
currentChunk++;
const progress = (currentChunk / totalChunks) * 100;
if (!data.success) {
throw new Error(data.error || 'Upload failed');
}
uploadedChunks++;
const progress = (uploadedChunks / totalChunks) * 100;
updateFixtureProgress(Math.round(progress));
statusDiv.textContent = `Uploading to all matches... ${Math.round(progress)}%`;
uploadChunk();
});
}
// Start uploading chunks in parallel
const promises = [];
for (let i = 0; i < Math.min(maxConcurrent, totalChunks); i++) {
promises.push(uploadNextChunk(i));
}
function uploadNextChunk(startIndex) {
let currentIndex = startIndex;
return new Promise((resolve, reject) => {
function uploadNext() {
if (currentIndex >= totalChunks || failed) {
resolve();
return;
}
uploadChunk(currentIndex)
.then(() => {
currentIndex += maxConcurrent;
if (currentIndex < totalChunks) {
uploadNext();
} else {
throw new Error(data.error || 'Upload failed');
resolve();
}
})
.catch(error => {
failed = true;
reject(error);
});
}
uploadNext();
});
}
Promise.all(promises)
.then(() => {
if (!failed) {
finalizeFixtureUpload(uploadId, file.name);
}
})
.catch(error => {
......@@ -745,9 +816,6 @@
});
}
uploadChunk();
}
function finalizeFixtureUpload(uploadId, fileName) {
const statusDiv = document.getElementById('fixture_upload_status');
......
......@@ -766,8 +766,10 @@
function uploadFileInChunks(file, matchId) {
const totalChunks = Math.ceil(file.size / CHUNK_SIZE);
const uploadId = Date.now() + '_' + Math.random().toString(36).substr(2, 9);
const maxConcurrent = 3; // Upload up to 3 chunks simultaneously
let currentChunk = 0;
let uploadedChunks = 0;
let failed = false;
// Show progress bar and hide upload form
const uploadForm = document.getElementById(`upload_form_${matchId}`);
......@@ -780,48 +782,80 @@
statusDiv.className = 'upload-status uploading';
statusDiv.textContent = 'Uploading... 0%';
function uploadChunk() {
if (currentChunk >= totalChunks) {
// All chunks uploaded, finalize
finalizeUpload(uploadId, file.name, matchId);
return;
}
function uploadChunk(chunkIndex) {
if (failed) return;
const start = currentChunk * CHUNK_SIZE;
const start = chunkIndex * CHUNK_SIZE;
const end = Math.min(start + CHUNK_SIZE, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append('chunk', chunk);
formData.append('chunkIndex', currentChunk);
formData.append('chunkIndex', chunkIndex);
formData.append('totalChunks', totalChunks);
formData.append('uploadId', uploadId);
formData.append('fileName', file.name);
formData.append('matchId', matchId);
fetch('/upload/chunk', {
return fetch('/upload/chunk', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
currentChunk++;
const progress = (currentChunk / totalChunks) * 100;
if (!data.success) {
throw new Error(data.error || 'Upload failed');
}
uploadedChunks++;
const progress = (uploadedChunks / totalChunks) * 100;
updateProgress(matchId, Math.round(progress));
statusDiv.textContent = `Uploading... ${Math.round(progress)}%`;
uploadChunk();
});
}
// Start uploading chunks in parallel
const promises = [];
for (let i = 0; i < Math.min(maxConcurrent, totalChunks); i++) {
promises.push(uploadNextChunk(i));
}
function uploadNextChunk(startIndex) {
let currentIndex = startIndex;
return new Promise((resolve, reject) => {
function uploadNext() {
if (currentIndex >= totalChunks || failed) {
resolve();
return;
}
uploadChunk(currentIndex)
.then(() => {
currentIndex += maxConcurrent;
if (currentIndex < totalChunks) {
uploadNext();
} else {
throw new Error(data.error || 'Upload failed');
resolve();
}
})
.catch(error => {
statusDiv.className = 'upload-status error';
statusDiv.textContent = 'Upload failed: ' + error.message;
failed = true;
reject(error);
});
}
uploadNext();
});
}
uploadChunk();
Promise.all(promises)
.then(() => {
if (!failed) {
finalizeUpload(uploadId, file.name, matchId);
}
})
.catch(error => {
statusDiv.className = 'upload-status error';
statusDiv.textContent = 'Upload failed: ' + error.message;
});
}
function finalizeUpload(uploadId, fileName, matchId) {
......
......@@ -985,14 +985,15 @@ def finalize_upload():
# Create a file-like object for the assembled file
class FileLike:
def __init__(self, path):
def __init__(self, path, filename):
self.path = path
self.name = file_name
self.filename = filename
self.name = filename
def save(self, dst):
shutil.move(self.path, dst)
mock_file = FileLike(final_path)
mock_file = FileLike(final_path, file_name)
if match_id:
# ZIP upload for match
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment