-
Notifications
You must be signed in to change notification settings - Fork 601
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Next.js application returns "complete request must include the checksum for each part" for MPU #6818
Comments
Running into exactly the same issue on |
@zshzbh Reduce the version to 3.717.0,effective |
Can you try 3.735 to see if this issue persists? |
I'm using |
Hi zshzbh I tested https://github.com/Mette1982/aws-s3-error with the version you provided and still get the same error. I update both client-s3 and lib-storage to that version. |
Hey guys, I tried on my end with version 3.735 and the latest version 3.744 and I'm still seeing the error. I was working on the workaround and my workaround works- ![]() I'm sharing my workaround with CRC32 calculation to match AWS's expected format. I'm using @Mette1982 's repo and did some changes in In "use client";
import {
ChecksumAlgorithm,
CompleteMultipartUploadCommand,
CreateMultipartUploadCommand,
S3Client,
UploadPartCommand,
} from "@aws-sdk/client-s3";
import { useState } from "react";
// CRC32 lookup table
const crc32Table = new Uint32Array(256);
for (let i = 0; i < 256; i++) {
let crc = i;
for (let j = 0; j < 8; j++) {
crc = (crc >>> 1) ^ ((crc & 1) ? 0xEDB88320 : 0);
}
crc32Table[i] = crc;
}
// Modified CRC32 calculation to match AWS's expected format
const calculateCRC32 = (chunk: Uint8Array): string => {
let crc = 0xFFFFFFFF;
// Process each byte
for (const byte of chunk) {
crc = (crc >>> 8) ^ crc32Table[(crc ^ byte) & 0xFF];
}
// Finalize CRC
crc = ~crc >>> 0; // Ensure unsigned 32-bit
// Convert to Base64
const buffer = new ArrayBuffer(4);
new DataView(buffer).setUint32(0, crc, false); // big-endian
return Buffer.from(buffer).toString('base64');
};
export default function UploadCmp() {
const [text, setText] = useState<string[]>([]);
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files) {
try {
const file = Array.from(e.target.files)[0];
const client = new S3Client({
region: "us-east-1",
credentials: {
accessKeyId: "xxxxx",
secretAccessKey: "xxxx",
},
});
// Step 1: Create multipart upload
const createResponse = await client.send(
new CreateMultipartUploadCommand({
Bucket: "test-s3-upload-xxx",
Key: "large-file.txt",
ChecksumAlgorithm: ChecksumAlgorithm.CRC32,
})
);
const uploadId = createResponse.UploadId;
const partSize = 5 * 1024 * 1024; // 5MB parts
const parts: { PartNumber: number; ETag: string; ChecksumCRC32: string }[] = [];
// Read file in chunks
const fileSize = file.size;
let partNumber = 1;
let bytesProcessed = 0;
while (bytesProcessed < fileSize) {
const end = Math.min(bytesProcessed + partSize, fileSize);
const chunk = await file.slice(bytesProcessed, end).arrayBuffer();
const chunkUint8 = new Uint8Array(chunk);
// Calculate CRC32 for the chunk
const checksum = calculateCRC32(chunkUint8);
// Upload part with checksum
const uploadResponse = await client.send(
new UploadPartCommand({
Bucket: "test-s3-upload-xx",
Key: "large-file.txt",
UploadId: uploadId,
PartNumber: partNumber,
Body: chunkUint8,
ChecksumAlgorithm: ChecksumAlgorithm.CRC32,
ChecksumCRC32: checksum,
})
);
parts.push({
PartNumber: partNumber,
ETag: uploadResponse.ETag!,
ChecksumCRC32: checksum,
});
// Update progress
bytesProcessed = end;
partNumber++;
const progressText = `Uploaded ${bytesProcessed} of ${fileSize} bytes`;
setText((prevState) => [...prevState, progressText]);
console.log(progressText);
}
// Complete multipart upload
await client.send(
new CompleteMultipartUploadCommand({
Bucket: "test-s3-upload-xx",
Key: "large-file.txt",
UploadId: uploadId,
MultipartUpload: {
Parts: parts.map(({ PartNumber, ETag, ChecksumCRC32 }) => ({
PartNumber,
ETag,
ChecksumCRC32,
})),
},
})
);
const doneMessage = "File uploaded successfully";
setText((prevState) => [...prevState, doneMessage]);
console.log(doneMessage);
} catch (error: any) {
setText((prevState) => [...prevState, "Upload error see console"]);
console.error("Upload error:", error);
if (error.message) console.error("Error message:", error.message);
if (error.code) console.error("Error code:", error.code);
}
}
};
return (
<div>
<input type="file" onChange={handleFileChange} />
<div>
{text.map((value, index) => (
<div key={index}>{value}</div>
))}
</div>
</div>
);
} I added this CRC32 Implementation : const crc32Table = new Uint32Array(256);
// ... table initialization
const calculateCRC32 = (chunk: Uint8Array): string => {
// Calculates CRC32 checksum and returns base64 encoded string
} This implements a CRC32 checksum calculator that AWS S3 uses for data integrity verification. And in const createResponse = await client.send(
new CreateMultipartUploadCommand({...})
); And splits file into 5MB chunks and uploads each part while (bytesProcessed < fileSize) {
// Calculate checksum for chunk
// Upload part with checksum
// Track progress
} And then completes multipart upload await client.send(
new CompleteMultipartUploadCommand({...})
); While investigating this issue, please let me know if this workaround works for you. |
Running into the exactly the same issue. I downgraded to {
"dependencies": {
"@aws-sdk/client-s3": "^3.726.1",
"@aws-sdk/lib-storage": "^3.726.1"
}
} |
@bobbydams you meant to write {
"dependencies": {
"@aws-sdk/client-s3": "3.726.1",
"@aws-sdk/lib-storage": "3.726.1"
}
} (without the |
I got the same error with v3.750.0, using the [example codehttps://github.com/Mette1982/aws-s3-error/blob/main/src/app/upload.tsx?rgh-link-date=2025-01-17T17%3A24%3A03.000Z). Only workaround that worked for me was pinning versions to 3.726.1 and removing the |
I was able to execute this code in a browser as of the latest AWS SDK version: import { S3Client } from "@aws-sdk/client-s3";
import { Upload } from "@aws-sdk/lib-storage";
const s3 = new S3Client({ region, credentials });
// log requests
s3.middlewareStack.add(
(next, context) => async (args) => {
const headers = {
...args.request.headers,
};
delete headers["x-amz-security-token"];
delete headers.authorization;
// console.log(context.commandName, "outgoing headers", headers);
const r = await next(args);
console.log(context.commandName, r.response.statusCode, r.response.headers);
return r;
},
{
step: "finalizeRequest",
}
);
// generate a stream in 8kb chunks
function stream(size) {
async function* generate() {
while (size > 0) {
yield "a".repeat(8 * 1024);
size -= 8 * 1024;
}
}
return new ReadableStream({
async start(controller) {
for await (const chunk of generate()) {
controller.enqueue(chunk);
}
controller.close();
},
});
}
// execute MPU
const params = {
Bucket: "...",
Key: "...",
Body: stream(6 * 1024 * 1024), // 6 MB
ChecksumAlgorithm: "CRC32",
};
await new Upload({
client: s3,
params,
}).done(); This is the CORS policy on my test bucket, did you enable this?
|
Checkboxes for prior research
Describe the bug
Original reported from @Mette1982 - #6810 (comment)
This works indeed perfectly on a NodeJs environment but if I execute the same in a NextJs (executed in the browser) app for instance it no longer works.
The user created a small NextJs app to demonstrate my issue using the code sample you provided => https://github.com/Mette1982/aws-s3-error
You only have to change values in the https://github.com/Mette1982/aws-s3-error/blob/main/src/app/upload.tsx file.
Dependencies -
Regression Issue
SDK version number
@aws-sdk/lib-storage; @aws-sdk/client-s3
Which JavaScript Runtime is this issue in?
Browser
Details of the browser/Node.js/ReactNative version
20.18
Reproduction Steps
Whole next js is in - https://github.com/Mette1982/aws-s3-error/blob/main/src/app/upload.tsx file.
The file that handle upload requeset -
Observed Behavior
Got error
Expected Behavior
expect it to work successfully
Possible Solution
No response
Additional Information/Context
No response
The text was updated successfully, but these errors were encountered: