You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:01:51