From 44c22bd95ec759b04b8cb638a3fafdac34b8072a Mon Sep 17 00:00:00 2001 From: Zac Date: Tue, 7 Apr 2026 00:52:54 -0400 Subject: [PATCH] Replace Upload class with PutObjectCommand for RustFS compatibility The @aws-sdk/lib-storage Upload class internally uses CreateMultipartUploadCommand for files over the part size threshold, which returns non-standard XML from RustFS causing UnknownError. PutObjectCommand does a simple single PUT request that RustFS handles correctly. Fixed in both /api/upload and /api/link/:id/upload endpoints. Co-Authored-By: Claude Opus 4.6 --- server.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/server.js b/server.js index c99b9e1..6a5f636 100644 --- a/server.js +++ b/server.js @@ -535,14 +535,14 @@ app.post("/api/upload", requireAuth, upload.array("files", 50), async (req, res) const key = prefix ? `${prefix.replace(/[-\/]+$/, "")}--${file.originalname}` : file.originalname; const contentType = getMimeType(file.originalname, file.mimetype); console.log(`Uploading: ${key} (${file.size} bytes, ${contentType})`); - const uploadPromise = new Upload({ - client: s3Client, - params: { Bucket: bucket, Key: key, Body: fs.createReadStream(file.path), ContentType: contentType }, - queueSize: 4, - partSize: 10 * 1024 * 1024, - leavePartsOnError: false, - }).done(); - const result = await withTimeout(uploadPromise, UPLOAD_TIMEOUT_MS, key); + // Use PutObjectCommand (single PUT) — compatible with RustFS/MinIO/generic S3. + // The @aws-sdk/lib-storage Upload class uses CreateMultipartUpload internally + // which returns non-standard XML from RustFS and causes UnknownError. + const fileBuffer = fs.readFileSync(file.path); + const putPromise = s3Client.send(new PutObjectCommand({ + Bucket: bucket, Key: key, Body: fileBuffer, ContentType: contentType, + })); + const result = await withTimeout(putPromise, UPLOAD_TIMEOUT_MS, key); if (result?.assumed) console.log(`Assumed success (timeout): ${key}`); else console.log(`Confirmed success: ${key}`); try { fs.unlinkSync(file.path); } catch (_) {} @@ -841,12 +841,11 @@ app.post("/api/sharelinks/:token/upload", upload.array("files", 50), async (req, for (const file of req.files) { const key = prefix ? `${prefix.replace(/[-\/]+$/, "")}--${file.originalname}` : file.originalname; const contentType = getMimeType(file.originalname, file.mimetype); - const uploadPromise = new Upload({ - client: s3Client, - params: { Bucket: bucket, Key: key, Body: fs.createReadStream(file.path), ContentType: contentType }, - queueSize: 4, partSize: 10 * 1024 * 1024, leavePartsOnError: false, - }).done(); - await withTimeout(uploadPromise, UPLOAD_TIMEOUT_MS, key); + const fileBuffer = fs.readFileSync(file.path); + const putPromise = s3Client.send(new PutObjectCommand({ + Bucket: bucket, Key: key, Body: fileBuffer, ContentType: contentType, + })); + await withTimeout(putPromise, UPLOAD_TIMEOUT_MS, key); try { fs.unlinkSync(file.path); } catch (_) {} results.push({ originalName: file.originalname, key, size: file.size }); }