JavaScript生成的S3 ManagedUpload的CheckSumSHA256始终无效问题求助
Amazon S3 ManagedUpload的CheckSumSHA256始终无效的排查与解决
我在用JavaScript实现Amazon S3 ManagedUpload时,生成的CheckSumSHA256始终无效。先计算文件的SHA256哈希值,再用btoa(sha256)转成base64编码,但一直收到错误:InvalidRequest: Value for x-amz-checksum-sha256 header is invalid.
AWS文档说明:
ChecksumSHA256 — (String) 对象经过base64编码的256位SHA-256摘要,仅在随对象上传时存在。对于分段上传,该值可能不是对象的校验和。有关分段上传校验和计算的更多信息,请参阅《Amazon S3用户指南》中的“检查对象完整性”。
当前上传参数代码:
let sha256conv = btoa(sha256); const params = { Bucket: process.env.BUCKET, Key: path.basename(task.file), Body: fileData, ContentType: ContentType || 'application/octet-stream', CacheControl: "max-age=172800", ChecksumAlgorithm: 'sha256', ChecksumSHA256: sha256conv }; const upload = new AWS.S3.ManagedUpload({ service: s3, params });
SHA256生成函数:
export async function getFileSha256(fileName, fileSize, onProgress) { return new Promise((resolve, reject) => { const hash = crypto.createHash('sha256'); // change to 'binary' if you want a binary hash. hash.setEncoding('hex'); const bar1 = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic); bar1.start(fileSize, 0); const fd = fs.createReadStream(fileName); fd.on('data', async chunk => { let processedBytes = bar1.value + chunk.length; bar1.update(processedBytes); await onProgress(processedBytes) }) fd.on('end', function () { bar1.stop(); hash.end(); return resolve(hash.read()) }); // read all file and pipe it (write it) to the hash object fd.pipe(hash); }); }
问题原因
- 哈希编码格式错配:生成函数中
hash.setEncoding('hex')输出的是十六进制字符串,而AWS要求的是原始二进制SHA256摘要的base64编码。直接对十六进制字符串用btoa()转码,得到的结果不符合S3的校验规则。 btoa()的使用场景错误:btoa()仅能处理ASCII字符串,对十六进制字符串转码无法得到原始二进制数据的base64结果。
解决方法
方案一:修改哈希生成逻辑(推荐)
直接生成二进制哈希值并转base64,无需额外转换:
export async function getFileSha256(fileName, fileSize, onProgress) { return new Promise((resolve, reject) => { const hash = crypto.createHash('sha256'); // 移除hex编码设置,保留原始二进制 const bar1 = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic); bar1.start(fileSize, 0); const fd = fs.createReadStream(fileName); fd.on('data', async chunk => { let processedBytes = bar1.value + chunk.length; bar1.update(processedBytes); await onProgress(processedBytes); }); fd.on('end', function () { bar1.stop(); hash.end(); // 读取二进制哈希并转base64 const buffer = hash.read(); resolve(buffer.toString('base64')); }); fd.pipe(hash); }); }
上传时直接使用返回结果,无需btoa():
const sha256conv = await getFileSha256(task.file, fileSize, onProgress); const params = { Bucket: process.env.BUCKET, Key: path.basename(task.file), Body: fileData, ContentType: ContentType || 'application/octet-stream', CacheControl: "max-age=172800", ChecksumAlgorithm: 'sha256', ChecksumSHA256: sha256conv };
方案二:保留十六进制生成逻辑,转换方式修正
如果不想修改哈希生成函数,将十六进制字符串转成二进制Buffer后再转base64:
// 替换原有的btoa(sha256) const sha256Buffer = Buffer.from(sha256, 'hex'); const sha256conv = sha256Buffer.toString('base64');
额外注意事项
- 确保
fileData与计算哈希时读取的文件内容完全一致,避免因文件读取编码、分段方式差异导致哈希不匹配。 - 若使用ManagedUpload的分段上传,S3可能不会将你提供的ChecksumSHA256作为对象最终校验和,需参考S3文档中分段上传的校验和计算规则。
内容的提问来源于stack exchange,提问作者DudiDude
相关产品推荐
相关产品推荐

