为什么必须用分块上传?
我的处理经验
关于分块上传,最值得先弄清楚的是配置边界和排错顺序。上传超过 100 MB 的文件时,传统的单一 PUT 请求面临三个致命问题:
- 网络抖动导致全量重传:一旦连接中断,整个文件就需要重新上传。
- 超时限制:大多数负载均衡器和 CDN 都有 30~60 秒的连接空闲超时,大文件很可能在传输中途被截断。
- 内存开销:客户端需要将整个文件读入内存,对于 2 GB 的文件,内存占用直接超过 2 GB。
分块上传(Multipart Upload)将文件切分为若干个较小的部分(通常 5 MB ~ 1 GB),每个分块独立发起 PUT 请求,最后服务端自动合并。这天然支持并行上传和断点续传——失败时只需重传未完成的分块,效率提升显著。
AWS S3 分块上传实现
1. 初始化与分块策略
S3 的分块上传分为三个阶段:CreateMultipartUpload → UploadPart → CompleteMultipartUpload。分块大小建议为 8 MB ~ 100 MB,总块数不超过 10000。
import boto3
from boto3.s3.transfer import TransferConfig
s3_client = boto3.client('s3')
response = s3_client.create_multipart_upload(
Bucket='my-bucket',
Key='large-file.zip'
)
upload_id = response['UploadId']
2. 并行上传与 ETag 捕获
每个分块上传后,S3 返回 ETag(MD5 哈希)。必须按分块编号保存这些 ETag,用于最后的合并请求。使用 Python 的 concurrent.futures 或 asyncio 实现并行。
import concurrent.futures
def upload_part(file_path, part_number, upload_id):
with open(file_path, 'rb') as f:
f.seek((part_number - 1) * part_size)
data = f.read(part_size)
response = s3_client.upload_part(
Bucket='my-bucket',
Key='large-file.zip',
PartNumber=part_number,
UploadId=upload_id,
Body=data
)
return {'PartNumber': part_number, 'ETag': response['ETag']}
with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor:
futures = [executor.submit(upload_part, file_path, i, upload_id) for i in range(1, total_parts+1)]
parts = [f.result() for f in concurrent.futures.as_completed(futures)]
3. 完成合并
s3_client.complete_multipart_upload(
Bucket='my-bucket',
Key='large-file.zip',
UploadId=upload_id,
MultipartUpload={'Parts': parts}
)
4. 断点续传实现
在上传过程中将 upload_id 和已完成的 parts 列表持久化到本地数据库或 Redis。如果程序中断,重新调用 list_parts 获取已上传的分块列表,只上传缺失的分块即可。注意:如果调用 abort_multipart_upload 会清除所有分块,需谨慎使用。
# 恢复时查询已有分块
resp = s3_client.list_parts(Bucket='my-bucket', Key='large-file.zip', UploadId=upload_id)
uploaded_parts = {p['PartNumber']: p['ETag'] for p in resp.get('Parts', [])}
# 仅上传 missing_parts
进阶阅读:此处可内链到“分块上传性能优化”指南。
阿里云 OSS 分块上传实现
1. 初始化与分块策略
OSS 的接口称为 InitiateMultipartUpload、UploadPart、CompleteMultipartUpload,语义与 S3 高度一致。官方 Python SDK(oss2)提供了更简洁的高层封装:
import oss2
auth = oss2.Auth('AccessKeyId', 'AccessKeySecret')
bucket = oss2.Bucket(auth, 'https://oss-cn-hangzhou.aliyuncs.com', 'my-bucket')
# 使用 oss2.resumable_upload 自动支持分块 + 断点续传
oss2.resumable_upload(bucket, 'large-file.zip', 'local-large-file.zip',
multipart_threshold=10*1024*1024, part_size=5*1024*1024,
num_threads=8)
resumable_upload 会在本地生成一个 checkpoint 文件(默认在 ~/.oss_upload/),记录已上传的分块。如果上传中断,下次调用同一文件会自动从断点继续。
2. 手动控制并发与分块校验
如果需要精细控制,可以像 S3 一样手动调用底层 API。OSS 每个分块上传后同样返回 ETag,但 ETag 是分块内容的 MD5 值(不含引号)。注意:OSS 要求分块大小必须大于 100 KB(最后一个分块除外),且除最后一块外最小为 100 KB。
upload_id = bucket.init_multipart_upload(key).upload_id
parts = []
for i in range(1, total_parts+1):
result = bucket.upload_part(key, upload_id, i, data)
parts.append(oss2.models.PartInfo(i, result.etag))
bucket.complete_multipart_upload(key, upload_id, parts)
关联教程:此处可内链到“分块上传部署与验证”内容。
相关阅读:此处可内链到“分块上传常见问题”专题。
关键参数与最佳实践
分块大小
分块越小,重传粒度越细,但分块数量增多会增加服务端合并的开销。推荐:
- 100 MB ~ 1 GB 文件:分块 8 MB
- 1 GB ~ 10 GB 文件:分块 16~32 MB
- 10 GB 以上:分块 64~100 MB(注意 S3 总块数不超过 10000)
并发数
并行上传分块能充分利用带宽,但过高的并发反而会导致 TCP 拥塞和 CPU 争抢。经验值:
- 客户端带宽 100 Mbps:并发 4~8
- 客户端带宽 500 Mbps:并发 8~16
- 服务器端限流时:逐级降低并发,观察 429 错误
超时与重试
每个分块的 HTTP 请求应设置合理的超时(如 60 秒)。捕获 socket.timeout 或 SDK 的 RequestTimeout 异常后,自动重试该分块(最多 3 次)。避免因一个分块失败导致整个上传任务放弃。
MD5 校验
部分云存储要求上传时提供分块的 Content-MD5。S3 的 UploadPart 会自动计算并校验;OSS 需要显式设置 Content-MD5 头。如果检测到分块损坏,立即重传。
常见问题与排查
错误码 400 EntityTooSmall
S3 要求除最后一个分块外,每个分块大小至少 5 MB。如果手动切分时使用了更小的尺寸,会收到该错误。解决方案:将小于 5 MB 的块合并到上一块或最后一块。
错误码 403 AccessDenied
分块上传涉及多个 API,需要 IAM 策略允许 s3:PutObject、s3:AbortMultipartUpload、s3:ListMultipartUploadParts 等操作。OSS 则需要 oss:PutObject 和 oss:InitiateMultipartUpload 权限。
上传速度不理想
检查客户端与存储节点之间的网络延迟和丢包率。使用 CDN 加速上传或选择离用户最近的区域(如 AWS 的 S3 Transfer Acceleration 或 OSS 的传输加速)。
总结
先看关键判断
分块上传不是锦上添花的优化,而是大文件传输的基础设施。无论是 AWS S3 还是阿里云 OSS,其 API 设计都支持从初始化到完成的完整流程,并且均提供了 SDK 级别的断点续传封装。在实际工程中,建议直接使用 SDK 自带的 resumable 函数(如 oss2.resumable_upload 或 boto3 的 TransferConfig 的 use_threads=True),它们已经处理了大部分边界情况。如果仍需定制,请务必关注分块大小、并发数、超时重试和 ETag 校验这四个核心参数。掌握这些,您的大文件上传功能将像本地拷贝一样可靠。
延伸阅读
