如何结合CompressionStream实现S3大文件分片压缩上传?
我正在研究将文件压缩后上传至S3的方案,发现CompressionStream API处理单文件上传流程很简单,但针对1-3GB的大文件,希望结合S3 Multipart Upload的优势。由于S3不支持直接流式上传,我尝试用TransformStream处理压缩后的流来实现分片上传,伪代码如下:
// 尝试用TransformStream实现,但欢迎更好的方案 // 仅为伪代码,file为用户选择的文件:event.target.files[0] file .stream() .pipeThrough(new CompressionStream('gzip')) .pipeThrough(new TransformStream({ start(){}, transform(chunk, controller) { uploadPromises.push( s3Client.send( new UploadPartCommand({ Bucket: bucketName, Key: key, UploadId: uploadId, Body: some_chunk_of_5mb_size, // 这里是困惑点 PartNumber: i + 1, }) ) ) }, }))
存在以下疑问:
- 如何获取≥5MB的分片以满足S3分片上传的要求?
- TransformStream中的
chunk数据类型是什么?能否检查大小并拼接成5MB分片? - 将分片转换为缓冲等操作是否会影响上传文件的完整性?
问题解答
1. 如何获取≥5MB的分片以满足S3分片上传要求?
你需要在TransformStream内部维护一个缓冲区,每次收到chunk就往缓冲区追加数据,当缓冲区总大小达到或超过5MB时,就把这个缓冲区作为一个分片发起上传,随后清空缓冲区继续接收后续流数据。等所有流数据处理完毕后,要检查缓冲区是否还有剩余数据——哪怕不足5MB(S3允许最后一个分片小于5MB),也要作为最后一个分片完成上传。
2. TransformStream中的chunk数据类型是什么?能否检查大小并拼接成5MB分片?
chunk的类型是Uint8Array(或其子类如Uint8ClampedArray),可以通过chunk.byteLength直接获取它的字节大小。完全可以把多个小chunk拼接成5MB的大分片:维护一个Uint8Array类型的缓冲区,每次新chunk进来时,计算当前缓冲区+新chunk的总长度,创建新的Uint8Array并把两者的数据复制进去,直到总长度≥5MB,就拆分出5MB的部分作为分片上传,剩余数据留在缓冲区继续拼接后续chunk。
3. 将分片转换为缓冲等操作是否会影响上传文件的完整性?
只要严格按照流的顺序拼接分片,且上传时未修改分片的字节内容,就不会影响文件完整性。S3 Multipart Upload最终会按PartNumber的顺序把所有分片拼接起来,只要每个分片的字节数据和压缩后的流完全一致,最终得到的文件就和直接上传完整压缩文件的结果完全相同。
额外优化建议:上传分片时最好控制并发数,不要一次性把所有分片Promise都存入数组,避免浏览器或S3的请求限制导致失败。可以用并发池机制,比如同时最多上传3个分片,等一个上传完成后再发起下一个。
内容的提问来源于stack exchange,提问作者haxwagores

