name: s3-bulk-upload description: 通过首字符前缀自动组织文件,批量上传到S3。 version: 1.0.0 metadata: openclaw: requires: env: - AWS_ACCESS_KEY_ID - AWS_SECRET_ACCESS_KEY bins: - aws primaryEnv: AWS_ACCESS_KEY_ID emoji: “\U0001F4E6” install: - kind: brew formula: awscli bins: [aws]
S3 批量上传
使用首字符前缀自动组织文件上传到S3(例如:a/apple.txt、b/banana.txt、0-9/123.txt)。
快速开始
使用附带的脚本进行批量上传:
# 基本上传
./s3-bulk-upload.sh ./files my-bucket
# 试运行预览
./s3-bulk-upload.sh ./files my-bucket --dry-run
# 使用同步模式(对大量文件更快)
./s3-bulk-upload.sh ./files my-bucket --sync
# 指定存储类别
./s3-bulk-upload.sh ./files my-bucket --storage-class STANDARD_IA
先决条件
验证AWS凭证已配置:
aws sts get-caller-identity
如果失败,请确保设置了 AWS_ACCESS_KEY_ID 和 AWS_SECRET_ACCESS_KEY,或通过 aws configure 配置。
组织逻辑
文件根据其文件名的第一个字符进行组织:
| 首字符 | 前缀 |
|---|---|
a-z |
小写字母(例如:a/、b/) |
A-Z |
小写字母(例如:a/、b/) |
0-9 |
0-9/ |
| 其他 | _other/ |
单文件上传
使用自动前缀上传单个文件:
FILE="example.txt"
BUCKET="my-bucket"
# 根据首字符计算前缀
FIRST_CHAR=$(echo "${FILE}" | cut -c1 | tr '[:upper:]' '[:lower:]')
if [[ "$FIRST_CHAR" =~ [a-z] ]]; then
PREFIX="$FIRST_CHAR"
elif [[ "$FIRST_CHAR" =~ [0-9] ]]; then
PREFIX="0-9"
else
PREFIX="_other"
fi
aws s3 cp "$FILE" "s3://${BUCKET}/${PREFIX}/${FILE}"
批量上传
上传目录中的所有文件:
SOURCE_DIR="./files"
BUCKET="my-bucket"
for FILE in "$SOURCE_DIR"/*; do
[ -f "$FILE" ] || continue
BASENAME=$(basename "$FILE")
FIRST_CHAR=$(echo "$BASENAME" | cut -c1 | tr '[:upper:]' '[:lower:]')
if [[ "$FIRST_CHAR" =~ [a-z] ]]; then
PREFIX="$FIRST_CHAR"
elif [[ "$FIRST_CHAR" =~ [0-9] ]]; then
PREFIX="0-9"
else
PREFIX="_other"
fi
aws s3 cp "$FILE" "s3://${BUCKET}/${PREFIX}/${BASENAME}"
done
高效批量同步
对于大型上传,使用符号链接暂存文件,然后使用 aws s3 sync:
SOURCE_DIR="./files"
STAGING_DIR="./staging"
BUCKET="my-bucket"
# 创建具有前缀结构的暂存目录
rm -rf "$STAGING_DIR"
mkdir -p "$STAGING_DIR"
for FILE in "$SOURCE_DIR"/*; do
[ -f "$FILE" ] || continue
BASENAME=$(basename "$FILE")
FIRST_CHAR=$(echo "$BASENAME" | cut -c1 | tr '[:upper:]' '[:lower:]')
if [[ "$FIRST_CHAR" =~ [a-z] ]]; then
PREFIX="$FIRST_CHAR"
elif [[ "$FIRST_CHAR" =~ [0-9] ]]; then
PREFIX="0-9"
else
PREFIX="_other"
fi
mkdir -p "$STAGING_DIR/$PREFIX"
ln -s "$(realpath "$FILE")" "$STAGING_DIR/$PREFIX/$BASENAME"
done
# 将整个暂存目录同步到S3
aws s3 sync "$STAGING_DIR" "s3://${BUCKET}/"
# 清理
rm -rf "$STAGING_DIR"
验证
按前缀列出文件:
BUCKET="my-bucket"
PREFIX="a"
aws s3 ls "s3://${BUCKET}/${PREFIX}/" --recursive
生成所有已上传文件的清单:
BUCKET="my-bucket"
aws s3 ls "s3://${BUCKET}/" --recursive | awk '{print $4}'
统计每个前缀的文件数:
BUCKET="my-bucket"
for PREFIX in {a..z} 0-9 _other; do
COUNT=$(aws s3 ls "s3://${BUCKET}/${PREFIX}/" --recursive 2>/dev/null | wc -l | tr -d ' ')
[ "$COUNT" -gt 0 ] && echo "$PREFIX: $COUNT files"
done
错误处理
常见问题及解决方案:
| 错误 | 原因 | 解决方案 |
|---|---|---|
AccessDenied |
权限不足 | 检查IAM策略是否对存储桶拥有 s3:PutObject 权限 |
NoSuchBucket |
存储桶不存在 | 创建存储桶或检查存储桶名称拼写 |
InvalidAccessKeyId |
凭证错误 | 验证 AWS_ACCESS_KEY_ID 是否正确 |
ExpiredToken |
会话令牌过期 | 刷新凭证或重新认证 |
批量上传前测试存储桶访问权限:
BUCKET="my-bucket"
echo "test" | aws s3 cp - "s3://${BUCKET}/_test_access.txt" && \
aws s3 rm "s3://${BUCKET}/_test_access.txt" && \
echo "存储桶访问正常"
存储类别
使用存储类别优化成本:
# 标准(默认)
aws s3 cp file.txt s3://bucket/prefix/file.txt
# 不频繁访问(存储成本更低,检索收费)
aws s3 cp file.txt s3://bucket/prefix/file.txt --storage-class STANDARD_IA
# Glacier即时检索(归档且快速访问)
aws s3 cp file.txt s3://bucket/prefix/file.txt --storage-class GLACIER_IR
# 智能分层(根据访问模式自动优化)
aws s3 cp file.txt s3://bucket/prefix/file.txt --storage-class INTELLIGENT_TIERING
在批量上传循环中添加 --storage-class,以优化不常访问文件的成本。