Node.js向S3上传大文件时合并分片遇InvalidPart: null错误
一、InvalidPart错误的核心原因及修复
你的代码存在三个关键问题,直接导致合并时触发InvalidPart错误:
1. 异步上传导致PartNumber重复/顺序混乱
readStream.on("data")会连续触发多个异步的uploadPart请求,但你在uploadPart的回调里才递增partNumber。这会导致多个请求共用同一个partNumber值(比如第一个分片的上传回调还没执行,第二个data事件就已触发,此时partNumber仍为1),最终上传的分片PartNumber重复,S3无法识别合法的分片序列。
修复方式:在触发uploadPart前就确定当前分片的PartNumber,通过闭包保存当前值避免异步干扰:
let partNumber = 1; let numPartsUploaded = 0; // 提前通过文件总大小计算总片数,不要用动态变化的bytesRead const totalParts = Math.ceil(fileSize / partSize); readStream.on("data", (chunk) => { const currentPartNumber = partNumber++; // 先获取当前分片号再递增 // 确保当前分片的参数使用正确的PartNumber const currentPartParams = { ...partParams, PartNumber: currentPartNumber, Body: chunk }; s3.uploadPart(currentPartParams, (err, data) => { if (err) { console.log("Error upload part", err); return; } console.log(`part ${currentPartNumber} uploaded successfully`); parts.push({ ETag: data.ETag, PartNumber: currentPartNumber }); numPartsUploaded++; // 用提前计算的总片数判断是否全部上传完成 if (numPartsUploaded === totalParts) { // S3要求合并时分片必须按PartNumber从小到大排序,显式排序避免异步回调乱序 parts.sort((a, b) => a.PartNumber - b.PartNumber); s3.completeMultipartUpload({ Bucket: "myBucketName", Key: "name", UploadId: uploadId, MultipartUpload: { Parts: parts }, }, (err, data) => { if (err) { console.log("error: ", err); return; } console.log("file uploaded successfully"); }); } }); });
2. 分片完成判断逻辑错误
你用Math.ceil(readStream.bytesRead/partSize)作为判断条件,但bytesRead会随读取进度动态变化,可能在最后一块数据读取完成时,这个值等于总片数,但此时前面的分片可能还未完成上传(因为uploadPart是异步操作),导致提前调用completeMultipartUpload,此时parts数组还未收集全所有分片的ETag,合并时触发错误。
修复方式:提前获取文件总大小,计算出固定的总片数totalParts,当numPartsUploaded等于totalParts时再触发合并操作。
3. 分片未按要求排序
S3明确要求completeMultipartUpload时,Parts数组必须按PartNumber从小到大排序。即使分片是按顺序上传的,也可能因异步回调的执行顺序导致数组乱序,因此必须显式排序。
二、关于S3分片不可见的疑问
是的,S3控制台不会显示未完成的Multipart Upload分片。这些分片属于未完成的上传会话,只有合并成功后才会生成完整的对象;如果调用abortMultipartUpload,这些分片会被自动清理。你可以通过以下方式查看未完成的上传会话:
- 在S3控制台目标桶页面,切换到「上传」选项卡查看「正在进行的上传」
- 使用AWS CLI命令:
aws s3api list-multipart-uploads --bucket myBucketName - 调用S3的
listMultipartUploadsAPI
其他代码小问题
代码里有几处consol.log拼写错误,应改为console.log,否则无法正常输出日志,不利于排查问题。
内容的提问来源于stack exchange,提问作者smaili

