diff --git a/public/index.html b/public/index.html
index 3468742..513c1d1 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1090,71 +1090,48 @@ async function startUpload() {
}
// ============================================================
-// PARALLEL CHUNK UPLOAD (Option 4 — HTTP parallelism, Aspera-class speed)
-// Slices each file into 32 MB chunks, uploads 6 concurrently via POST,
-// server proxies to S3 multipart. Same approach as MASV — no UDP needed.
+// SIMPLE UPLOAD — uses /api/upload with @aws-sdk/lib-storage on server
+// Compatible with RustFS / MinIO / generic S3 endpoints.
+// XMLHttpRequest used for upload progress tracking.
// ============================================================
-const CHUNK_SIZE = 32 * 1024 * 1024; // 32 MB per part
-const CHUNK_WORKERS = 6; // concurrent chunk POSTs per file
-async function uploadFileChunked(item, idx) {
- const file = item.file;
- const totalParts = Math.ceil(file.size / CHUNK_SIZE);
- const mime = file.type || 'application/octet-stream';
+async function uploadFileDirect(item, idx) {
+ return new Promise((resolve, reject) => {
+ const fd = new FormData();
+ fd.append('prefix', selectedPrefix);
+ fd.append('files', item.file, item.name);
- // 1. Initiate S3 multipart upload
- const init = await api('POST','/api/upload/initiate',{
- filename: item.name, prefix: selectedPrefix,
- contentType: mime, totalParts,
+ const xhr = new XMLHttpRequest();
+ const pb = document.getElementById(`progbar-${idx}`);
+
+ xhr.upload.addEventListener('progress', (e) => {
+ if (e.lengthComputable) {
+ const pct = Math.round(e.loaded / e.total * 100);
+ if (pb) pb.style.width = pct + '%';
+ setFileStatus(idx, 'uploading', pct + '%');
+ }
+ });
+
+ xhr.addEventListener('load', () => {
+ try {
+ const data = JSON.parse(xhr.responseText);
+ if (xhr.status >= 200 && xhr.status < 300 && data.success) {
+ resolve(data);
+ } else {
+ reject(new Error(data.error || `Server returned ${xhr.status}`));
+ }
+ } catch (e) {
+ reject(new Error(`Bad response: ${xhr.status}`));
+ }
+ });
+
+ xhr.addEventListener('error', () => reject(new Error('Network error')));
+ xhr.addEventListener('abort', () => reject(new Error('Upload aborted')));
+
+ xhr.open('POST', '/api/upload');
+ xhr.setRequestHeader('x-auth-token', authToken);
+ xhr.send(fd);
});
- if (!init.success) throw new Error(init.error || 'Failed to initiate upload');
- const { uploadId } = init;
-
- const pb = document.getElementById(`progbar-${idx}`);
- let done = 0;
-
- // 2. Upload all parts with bounded concurrency
- const queue = Array.from({length: totalParts}, (_,i) => i+1); // 1-indexed
-
- async function worker() {
- while (queue.length) {
- const partNumber = queue.shift();
- if (partNumber === undefined) break;
- const start = (partNumber - 1) * CHUNK_SIZE;
- const blob = file.slice(start, Math.min(start + CHUNK_SIZE, file.size));
- const fd = new FormData();
- fd.append('uploadId', uploadId);
- fd.append('partNumber', String(partNumber));
- fd.append('chunk', blob, item.name);
- const resp = await fetch('/api/upload/chunk', {
- method: 'POST',
- headers: { 'x-auth-token': authToken },
- body: fd,
- });
- const data = await resp.json();
- if (!data.success) throw new Error(`Part ${partNumber} failed: ${data.error}`);
- done++;
- const pct = Math.round(done / totalParts * 100);
- if (pb) pb.style.width = pct + '%';
- setFileStatus(idx, 'uploading', pct + '%');
- }
- }
-
- try {
- await Promise.all(Array.from({length: Math.min(CHUNK_WORKERS, totalParts)}, worker));
- } catch (err) {
- // Abort orphaned multipart upload on any chunk failure
- fetch('/api/upload/abort', {
- method: 'POST',
- headers: { 'Content-Type': 'application/json', 'x-auth-token': authToken },
- body: JSON.stringify({ uploadId }),
- }).catch(() => {});
- throw err;
- }
-
- // 3. Complete the multipart upload
- const complete = await api('POST','/api/upload/complete',{ uploadId });
- if (!complete.success) throw new Error(complete.error || 'Failed to complete upload');
}
async function uploadHTTP(files) {
@@ -1170,7 +1147,7 @@ async function uploadHTTP(files) {
setFileStatus(idx,'uploading','Uploading…');
document.getElementById(`prog-${idx}`).style.display='block';
try {
- await uploadFileChunked(item, idx);
+ await uploadFileDirect(item, idx);
document.getElementById(`progbar-${idx}`).style.width='100%';
setFileStatus(idx,'done','✓ Done'); item.status='done';
showToast(`Uploaded: ${item.name}`,'success');