如何用Python Boto3为AWS S3浏览器分段上传的各部分签名?
搞定S3分片上传的预签名URL生成方案
嘿,我来帮你捋清楚怎么用Boto3给每个分片生成预签名URL,完成从浏览器+jQuery到S3的多部分上传!你已经拿到UploadId了,这就成功了一半,接下来分服务端和前端两部分来实现:
一、服务端:用Boto3生成分片预签名URL
核心是用Boto3的generate_presigned_url方法,指定upload_part这个客户端方法,然后传入必要的参数(Bucket、Key、UploadId、PartNumber)。这里我给你写个可复用的函数:
import boto3 def generate_presigned_part_urls(bucket_name, object_key, upload_id, num_parts): # 初始化S3客户端(确保你的环境已经配置好AWS凭证,比如通过环境变量或~/.aws/credentials) s3_client = boto3.client('s3') part_urls = [] # 为每个分片生成预签名URL,PartNumber从1开始递增 for part_number in range(1, num_parts + 1): presigned_url = s3_client.generate_presigned_url( ClientMethod='upload_part', Params={ 'Bucket': bucket_name, 'Key': object_key, 'UploadId': upload_id, 'PartNumber': part_number }, ExpiresIn=3600 # URL有效期,建议设1-6小时,避免上传过程中过期 ) part_urls.append({ 'part_number': part_number, 'url': presigned_url }) return part_urls
关键注意点:
- PartNumber必须从1开始:S3要求分片编号是连续的正整数,不能从0开始
- ExpiresIn设置合理:如果是大文件,上传时间久,就把有效期设长一点(比如3600*3=3小时)
- AWS凭证配置:确保服务端的AWS权限足够(需要
s3:PutObject和s3:UploadPart权限)
你可以把这个函数封装成一个API接口(比如用Flask/Django),让前端请求获取所有分片的预签名URL。
二、前端:用jQuery分片上传文件
前端需要做三件事:拆分文件、用预签名URL上传每个分片、最后通知服务端完成多部分上传。这里是完整的示例代码:
// 基础配置,根据你的实际情况修改 const BUCKET_NAME = 'your-bucket-name'; const OBJECT_KEY = 'uploads/your-file-name.ext'; // S3上的文件路径 const UPLOAD_ID = 'your-upload-id-from-create-multipart'; // 你已经拿到的UploadId const PART_SIZE = 5 * 1024 * 1024; // 每个分片大小(5MB,S3要求除最后一片外,其他分片至少5MB) // 计算分片总数 function calculatePartCount(file) { return Math.ceil(file.size / PART_SIZE); } // 分片上传主函数 async function uploadFileParts(file) { try { const partCount = calculatePartCount(file); // 1. 从服务端获取所有分片的预签名URL const presignedResponse = await $.ajax({ url: '/api/get-presigned-parts', // 你的服务端接口地址 method: 'POST', data: JSON.stringify({ bucket: BUCKET_NAME, key: OBJECT_KEY, upload_id: UPLOAD_ID, num_parts: partCount }), contentType: 'application/json' }); const partUrls = presignedResponse.part_urls; // 2. 逐个上传分片,记录每个分片的ETag const uploadedParts = []; for (let i = 0; i < partCount; i++) { const partNumber = i + 1; const start = i * PART_SIZE; const end = Math.min(start + PART_SIZE, file.size); const fileBlob = file.slice(start, end); // 切割文件 // 用预签名URL发送PUT请求上传分片(注意是PUT,不是POST!) const uploadResponse = await $.ajax({ url: partUrls[i].url, method: 'PUT', data: fileBlob, processData: false, // 不要处理Blob数据 contentType: file.type || 'application/octet-stream' // 保持文件原类型 }); // 从响应头获取ETag,注意要去掉前后的引号 const eTag = uploadResponse.getResponseHeader('ETag').replace(/"/g, ''); uploadedParts.push({ PartNumber: partNumber, ETag: eTag }); } // 3. 通知服务端完成多部分上传 await $.ajax({ url: '/api/complete-multipart', // 你的服务端完成上传接口 method: 'POST', data: JSON.stringify({ bucket: BUCKET_NAME, key: OBJECT_KEY, upload_id: UPLOAD_ID, parts: uploadedParts }), contentType: 'application/json' }); alert('文件上传成功!'); } catch (error) { console.error('上传失败:', error); // 可选:如果失败,可以调用服务端接口取消上传,避免S3保留无效的分片 // await $.ajax({url: '/api/abort-multipart', method: 'POST', ...}) } } // 绑定文件选择事件 $('#file-upload-input').on('change', function(e) { const selectedFile = e.target.files[0]; if (selectedFile) { uploadFileParts(selectedFile); } });
三、服务端完成多部分上传的接口
最后,你需要一个接口来调用Boto3的complete_multipart_upload方法,把所有分片的ETag和编号传给S3,完成整个文件的组装:
def complete_multipart_upload(bucket_name, object_key, upload_id, parts): s3_client = boto3.client('s3') response = s3_client.complete_multipart_upload( Bucket=bucket_name, Key=object_key, UploadId=upload_id, MultipartUpload={ 'Parts': parts } ) return response
额外提醒:
- 如果上传过程中出错,记得调用
abort_multipart_upload取消上传,避免S3存储无效的分片占用空间 - 可以给前端加上传进度条,通过监听每个分片的上传进度来更新
- 测试的时候先上传小文件,确认流程没问题再试大文件
内容的提问来源于stack exchange,提问作者Varun Singh
相关产品推荐
相关产品推荐

