Mô tả lỗi
Bạn đang upload file lên S3 và nhận được thông báo sau:
An error occurred (EntityTooLarge) when calling the PutObject operation:
Your proposed upload exceeds the maximum allowed object size.
Upload dừng hẳn. File không bao giờ vào được bucket.
Nguyên nhân
S3 có hai giới hạn kích thước khiến nhiều người vấp phải:
- Upload PUT đơn lẻ: tối đa 5 GB mỗi request
- Kích thước object tối đa: 5 TB (chỉ dùng multipart upload)
Vượt giới hạn bằng lệnh PutObject thông thường — qua SDK, lệnh s3api put-object của CLI, hay HTTP PUT thuần — AWS sẽ từ chối ngay lập tức. Không có upload một phần, không retry. Chỉ là lỗi này.
Multipart upload là giải pháp. Nó chia file thành các phần nhỏ ít nhất 5 MB mỗi phần (phần cuối có thể nhỏ hơn), đẩy lên song song, và S3 ghép lại ở phía server. Dump database 50 GB, kho lưu video 200 GB — đều hoạt động tốt theo cách này.
Cách 1: AWS CLI (Khuyên dùng khi upload nhanh)
Tin vui: aws s3 cp và aws s3 sync đã tự động xử lý multipart. Nếu bạn gặp lỗi này từ CLI, có thể bạn đang dùng s3api put-object — hãy chuyển sang các lệnh cấp cao hơn.
# Tự động dùng multipart cho file > 8 MB theo mặc định
aws s3 cp /path/to/largefile.tar.gz s3://your-bucket/largefile.tar.gz
Muốn tinh chỉnh kích thước từng phần? Phần lớn hơn đồng nghĩa ít request hơn, phù hợp khi kết nối nhanh:
aws s3 cp /path/to/largefile.tar.gz s3://your-bucket/largefile.tar.gz \
--multipart-threshold 64MB \
--multipart-chunksize 64MB
Kiểm tra cấu hình hiện tại:
aws configure get s3.multipart_threshold
aws configure get s3.multipart_chunksize
Hoặc đặt cố định trong file config AWS:
[profile default]
s3 =
multipart_threshold = 64MB
multipart_chunksize = 64MB
max_concurrent_requests = 10
Cách 2: boto3 — TransferConfig cho upload từ code
Đang dùng put_object() trong code? Đó chính là thủ phạm. Thay bằng upload_file() hoặc upload_fileobj() kết hợp với TransferConfig:
import boto3
from boto3.s3.transfer import TransferConfig
s3 = boto3.client('s3')
config = TransferConfig(
multipart_threshold=1024 * 1024 * 64, # 64 MB
multipart_chunksize=1024 * 1024 * 64, # mỗi phần 64 MB
max_concurrency=10,
use_threads=True
)
s3.upload_file(
Filename='/path/to/largefile.tar.gz',
Bucket='your-bucket',
Key='largefile.tar.gz',
Config=config
)
print('Upload hoàn tất')
Upload từ stream hoặc file object? Dùng upload_fileobj() thay thế:
with open('/path/to/largefile.tar.gz', 'rb') as f:
s3.upload_fileobj(f, 'your-bucket', 'largefile.tar.gz', Config=config)
Cả hai phương thức đều xử lý multipart tự động khi file vượt ngưỡng. put_object() luôn thực hiện một lần PUT duy nhất — và sẽ thất bại với bất kỳ file nào trên 5 GB.
Cách 3: Multipart Upload thủ công qua boto3 API
Cần kiểm soát toàn bộ? Logic retry tùy chỉnh, upload có thể tiếp tục, theo dõi tiến trình từng phần — đây là cách tiếp cận phù hợp:
import boto3
import os
import math
s3 = boto3.client('s3')
BUCKET = 'your-bucket'
KEY = 'largefile.tar.gz'
FILE_PATH = '/path/to/largefile.tar.gz'
CHUNK_SIZE = 64 * 1024 * 1024 # 64 MB
# 1. Khởi tạo upload
mpu = s3.create_multipart_upload(Bucket=BUCKET, Key=KEY)
upload_id = mpu['UploadId']
parts = []
try:
file_size = os.path.getsize(FILE_PATH)
total_parts = math.ceil(file_size / CHUNK_SIZE)
with open(FILE_PATH, 'rb') as f:
for part_number in range(1, total_parts + 1):
data = f.read(CHUNK_SIZE)
response = s3.upload_part(
Bucket=BUCKET,
Key=KEY,
PartNumber=part_number,
UploadId=upload_id,
Body=data
)
parts.append({'PartNumber': part_number, 'ETag': response['ETag']})
print(f'Đã upload phần {part_number}/{total_parts}')
# 2. Hoàn tất upload
s3.complete_multipart_upload(
Bucket=BUCKET,
Key=KEY,
UploadId=upload_id,
MultipartUpload={'Parts': parts}
)
print('Multipart upload hoàn tất')
except Exception as e:
# 3. Luôn hủy khi thất bại — upload chưa hoàn thành vẫn bị tính phí lưu trữ
s3.abort_multipart_upload(Bucket=BUCKET, Key=KEY, UploadId=upload_id)
raise e
Kiểm tra kết quả
Sau khi upload hoàn tất, xác nhận object đã tồn tại và kích thước khớp với file gốc:
# Kiểm tra object tồn tại và xem kích thước
aws s3 ls s3://your-bucket/largefile.tar.gz
# So sánh với file local
ls -lh /path/to/largefile.tar.gz
# Xem metadata của object
aws s3api head-object --bucket your-bucket --key largefile.tar.gz
Object được upload bằng multipart có ETag đặc trưng — kết thúc bằng -N trong đó N là số phần (ví dụ: "abc123-12" cho upload 12 phần). Bạn cũng sẽ thấy x-amz-mp-parts-count trong phản hồi metadata.
Phòng tránh
- Bỏ
put_object()cho bất kỳ file nào có thể trở nên lớn. Dùngupload_file()vớiTransferConfiglàm mặc định — xử lý tốt cả file nhỏ lẫn lớn. - Đặt lifecycle rule để tự động hủy multipart upload chưa hoàn thành. Upload thất bại để lại các phần mồ côi trong S3 và bạn vẫn bị tính phí. Thiết lập rule dọn dẹp sau 7 ngày rất đơn giản:
aws s3api put-bucket-lifecycle-configuration \
--bucket your-bucket \
--lifecycle-configuration '{
"Rules": [{
"ID": "abort-incomplete-multipart",
"Status": "Enabled",
"Filter": {"Prefix": ""},
"AbortIncompleteMultipartUpload": {"DaysAfterInitiation": 7}
}]
}'
- Thỉnh thoảng kiểm tra các upload chưa hoàn thành:
aws s3api list-multipart-uploads --bucket your-bucket

