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

使用续传令牌从S3下载文件时出现错误的技术求助

解决S3批量小文件下载中的续传令牌问题

我来帮你搞定这个S3批量小文件下载的坑!你的代码目前有两个核心问题:没正确处理S3分页的续传令牌,导致无法遍历所有对象;另外代码只实现了列出对象Key到CSV,还没完成实际下载逻辑。下面一步步给你修复和完善:

问题分析

  1. 续传令牌缺失:S3的list_objects_v2默认每次最多返回1000个对象,当桶里的文件超过这个数时,会返回NextContinuationToken,你必须把这个令牌传入下一次请求才能获取剩余对象。你的代码里完全没处理这个,所以要么只能拿到前1000个对象,要么循环会提前终止。
  2. 无实际下载逻辑:你目前只是把Key写入CSV,但没实现下载文件的代码。
  3. CSV写入不规范:每个Key后面没加换行,导致所有Key都挤在一起,后续根本没法用。

修复后的完整代码

import boto3
import os
from botocore.exceptions import ClientError

def download_s3_small_files(bucket, local_root_dir='/Users/hr/Desktop/s3_backup'):
    # 初始化S3客户端
    s3 = boto3.client('s3')
    kwargs = {'Bucket': bucket}
    
    # 创建本地根目录(如果不存在)
    os.makedirs(local_root_dir, exist_ok=True)
    
    # 保存对象Key的CSV路径
    csv_path = os.path.join(local_root_dir, 'files.csv')
    
    with open(csv_path, 'w') as file:
        while True:
            try:
                # 发起S3对象列表请求
                resp = s3.list_objects_v2(**kwargs)
            except ClientError as e:
                print(f"获取对象列表时出错: {e.response['Error']['Message']}")
                return
            
            # 检查是否有对象内容
            if 'Contents' not in resp:
                print("没有更多对象可遍历")
                break
            
            # 遍历当前页的所有对象
            for obj in resp['Contents']:
                key = obj['Key']
                # 写入CSV时添加换行
                file.write(f"{key}\n")
                
                # 构建本地保存路径
                local_file_path = os.path.join(local_root_dir, key)
                # 创建文件所在的目录(如果不存在)
                local_dir = os.path.dirname(local_file_path)
                os.makedirs(local_dir, exist_ok=True)
                
                try:
                    # 下载文件到本地
                    s3.download_file(bucket, key, local_file_path)
                    print(f"成功下载: {key}")
                except ClientError as e:
                    print(f"下载失败 {key}: {e.response['Error']['Message']}")
            
            # 检查是否有续传令牌,准备下一页请求
            if 'NextContinuationToken' in resp:
                kwargs['ContinuationToken'] = resp['NextContinuationToken']
            else:
                # 没有更多页,退出循环
                break

# 调用函数,替换成你的桶名
download_s3_small_files(bucket='your-bucket-name')

关键修复点说明

  • 续传令牌处理:每次请求后检查resp['NextContinuationToken'],如果存在就把它放到kwargs里,下一次请求会自动获取下一页的对象,直到没有令牌为止,确保遍历桶里所有对象。
  • 本地目录自动创建:用os.makedirs(..., exist_ok=True)自动创建S3 Key对应的目录结构,避免下载时因为目录不存在报错。
  • 完善的异常处理:捕获ClientError来处理S3权限不足、对象不存在等常见问题,方便排查错误。
  • 规范的CSV写入:每个Key后面添加换行符,保证CSV文件的可读性。

额外优化建议

如果你的小文件数量特别多(比如几万甚至几十万),可以考虑:

  • 多线程/多进程下载:用concurrent.futures.ThreadPoolExecutor来并行下载,提升速度(小文件适合多线程,因为IO等待时间长)。
  • 调整分页大小:在kwargs里添加MaxKeys=1000(最大允许值),减少请求次数。
  • 日志记录:把下载成功/失败的日志写入文件,方便后续核对。

内容的提问来源于stack exchange,提问作者Hazzamataza

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:22:39