如何下载数百万个S3文件并实时压缩后保存?
实现S3桶文件实时下载并压缩的方案
首先得指出你给出的示例命令的问题:aws s3 cp --recursive s3://bucket | gzip > file 没法工作,因为aws s3 cp --recursive会直接把文件下载到本地磁盘,而不是将文件内容输出到标准输出(stdout),所以管道接不到任何有效数据。要实现边下载边实时压缩,我们需要把每个文件的内容输出到stdout,再统一收集压缩,同时还要考虑是否保留原文件的目录结构。
下面分两种常见场景给出解决方案:
场景1:将所有文件内容合并为一个压缩包(不保留目录结构)
如果不需要还原原有的文件层级,只是把所有文件的内容合并成一个gzip压缩文件,可以用以下命令:
# 替换为你的S3桶名称 BUCKET_NAME="your-bucket-name" aws s3 ls "s3://$BUCKET_NAME/" --recursive --output json | jq -r '.Key' | while read -r file_key; do # 将每个文件的内容输出到stdout aws s3 cp "s3://$BUCKET_NAME/$file_key" - done | gzip > all-s3-files.gz
细节说明:
- 用
--output json结合jq提取文件路径(Key字段),可以避免文件名包含空格、特殊字符时的解析错误 aws s3 cp ... -中的-表示将文件内容输出到标准输出,而不是保存到本地文件- 所有文件的内容会通过管道传递给
gzip,实时压缩后保存到all-s3-files.gz
场景2:保留原目录结构的tar.gz压缩包(推荐)
如果需要解压后还原S3桶内的文件层级,推荐将文件打包为tar.gz格式,同时实时压缩:
# 替换为你的S3桶名称 BUCKET_NAME="your-bucket-name" # 最终压缩包文件名 BACKUP_FILE="s3-bucket-backup.tar.gz" # 创建一个命名管道,用于tar和gzip的流式传输 mkfifo tar_pipe # 启动gzip进程,从管道读取tar归档内容并压缩 gzip < tar_pipe > "$BACKUP_FILE" & # 遍历所有文件,将每个文件添加到tar归档中 aws s3 ls "s3://$BUCKET_NAME/" --recursive --output json | jq -r '.Key' | while read -r file_key; do # 将下载的文件内容作为指定路径的文件添加到tar归档 aws s3 cp "s3://$BUCKET_NAME/$file_key" - | tar --append --file=tar_pipe --transform="s|^|$file_key|" done # 关闭管道,等待gzip进程完成压缩 rm tar_pipe wait
细节说明:
- 命名管道(
mkfifo)让tar和gzip可以流式协作,不需要占用额外磁盘空间存储未压缩的tar包 tar --transform参数会将从stdin读取的内容映射为$file_key路径的文件,确保归档内保留原S3的目录结构- 最后用
wait等待gzip进程完成,避免压缩包损坏
优化建议:提升处理速度
如果桶内有上百万个文件,串行处理会很慢,可以考虑以下优化:
- 增加AWS CLI的并发数:在
aws s3 cp命令中添加--parallel参数(需要AWS CLI v2+),同时设置--max-concurrent-requests提升下载并发:aws s3 cp "s3://$BUCKET_NAME/$file_key" - --parallel --max-concurrent-requests 20 - 避免文件名解析错误:始终用
--output json+jq的方式提取文件路径,不要用awk处理原始输出(原始输出的文件名如果有空格会被分割)
内容的提问来源于stack exchange,提问作者helmocilta
相关产品推荐
相关产品推荐

