使用boto3向S3分段上传大文件时遭遇“Part number must be an integer between 1 and 10000”错误的原因及解决咨询
看起来你遇到的问题核心在于预估的文件大小和实际要上传的文件真实大小不匹配,导致实际需要的分块数量超过了S3允许的10000个分块上限,触发了这个错误。我来帮你拆解一下原因和解决办法:
为什么会出现这个错误?
S3的分段上传有两个硬性限制:最多只能有10000个分块,且每个分块(除了最后一个)的大小不能小于5MB。你现在的计算逻辑是基于**上一次备份的大小增加10%**来预估当前文件的大小,但如果当前要上传的加密数据库dump真实大小比这个预估的值更大,那么按照你计算的part_size来拆分,最终需要的分块数量就会超过10000。这时候boto3尝试创建编号超过10000的分块,就会被S3拒绝,抛出那个错误。
举个例子:如果真实文件大小是86GB,用你计算的8504036字节的part_size来拆分,总块数大概是86*1024*1024*1024 / 8504036 ≈ 10600,已经超过了10000的上限,自然会报错。
你提到把max_parts改成1000就没问题,是因为这时候计算出来的part_size更大(约85MB),哪怕真实文件大一点,分块数也不会超过1000,所以不会触发限制。
解决办法
1. 获取真实的文件/流大小(最可靠的方案)
如果你的ciphertext_stream是从本地文件生成的,或者可以提前获取到加密后的真实大小,一定要用真实大小来计算part_size,而不是预估。比如:
from boto3.s3.transfer import TransferConfig import os def upload_encrypted_dbdump(self, ciphertext_stream, encrypted_file_path): s3 = self.s3_session_client() # 获取加密文件的真实大小 real_total_size = os.path.getsize(encrypted_file_path) max_parts = 10000 # 最小分块大小不能低于S3要求的5MB min_part_size = 5 * 1024 * 1024 # 5242880字节 # 计算需要的分块大小,向上取整,同时不小于最小分块大小 part_size = (real_total_size + max_parts - 1) // max_parts part_size = max(part_size, min_part_size) print(f"Calculated part size: {part_size} bytes") s3_response = s3.upload_fileobj( Fileobj=ciphertext_stream, Bucket=self.s3_bucket, Key=f'{self.s3_folder}/{self.s3_dbdump_name}', ExtraArgs={'ACL': 'bucket-owner-full-control'}, Config=TransferConfig(multipart_chunksize=part_size) )
如果加密流无法直接获取大小,你可以先把加密后的内容写入临时文件,获取大小后再上传,或者在加密过程中记录下加密后的总大小。
2. 增加预估的余量,确保分块数不超限
如果实在没办法获取真实大小,那就要给预估的大小留足够的余量,比如把增量从10%提高到20%甚至30%,确保即使真实文件比预估的大,分块数也不会超过10000:
prev_total_size = 77309411328 # 用20%的增量做预估,留足余量 adjusted_size = int(prev_total_size * 1.2) max_parts = 10000 min_part_size = 5 * 1024 * 1024 part_size = (adjusted_size + max_parts - 1) // max_parts part_size = max(part_size, min_part_size)
3. 强制限制分块数不超过10000的兜底逻辑
你可以在代码里加一个校验,确保计算出来的part_size对应的分块数不会超过10000:
estimated_parts = (real_total_size + part_size - 1) // part_size if estimated_parts > 10000: # 重新计算更大的part_size,确保分块数≤10000 part_size = (real_total_size + 9999) // 10000 part_size = max(part_size, min_part_size)
额外提醒
一定要确保计算出来的part_size不小于5MB,这是S3对非最后一个分块的硬性要求,如果你的part_size小于这个值,即使分块数没超10000,也会触发其他错误。
备注:内容来源于stack exchange,提问作者tkyass

