sail — S3 对象存储 CLI
基于标准 S3 协议的命令行工具。单个静态二进制,零运行时依赖,跨平台开箱即用。
sail 面向任何兼容 S3 协议的对象存储服务(AWS S3、MinIO、阿里云 OSS 以及各类自建 S3 兼容服务),提供上传/下载、列举、删除、复制/移动、内容查看、预签名 URL 等日常操作。它不绑定任何特定云厂商,配置即用。
特性
- 标准 S3 协议:path-style + SigV4 签名,兼容 AWS S3 / MinIO / 阿里云 OSS 及各类自建 S3 兼容服务
- 丰富的传输:单文件、目录递归、管道流式上传,大文件自动分片
- 对象管理:下载、列举(长格式/目录视图/树形)、删除、复制/移动(本地↔s3、s3↔s3 服务端复制零带宽)
- 内容查看:多格式智能渲染——文本/JSON/YAML/CSV/XML/图片终端字符画/二进制
- 访问地址:预签名 URL、基于 CDN 域名的公开访问地址
- 多 profile 配置:prod / test / staging 等多环境切换,密钥可引用环境变量避免明文
- 跨平台:macOS / Linux,单二进制下载即用;支持 shell 自动补全(zsh / bash / fish)
安装
方式一:npm 安装(推荐,跨平台)
npm install -g @becrafter/sail
npm 会按操作系统和 CPU 架构自动只下载一个匹配的平台二进制,sail 命令开箱即用。支持 macOS(arm64/x64)、Linux(arm64/x64),二进制托管在 npm registry,无需额外联网下载。
方式二:下载二进制
到 Releases 页面下载对应平台的二进制,放入 PATH 即可。
方式三:go install
go install github.com/BeCrafter/sail@latest
方式四:源码构建
git clone https://github.com/BeCrafter/sail.git
cd sail && go build -o sail .
配置
快速初始化
sail config init
交互式生成 ~/.sail/config.yaml,并可选安装 shell 自动补全:
# 密钥可用 ${VAR} 引用环境变量,避免明文。
default-profile: prod
profiles:
prod:
endpoint: <your-s3-endpoint>
access-key: ${SAIL_ACCESS_KEY}
secret-key: ${SAIL_SECRET_KEY}
bucket: ""
region: ""
path-style: true
cdn-domain: <your-cdn-domain>
test:
endpoint: <your-s3-endpoint-test>
access-key: ${SAIL_TEST_ACCESS_KEY}
secret-key: ${SAIL_TEST_SECRET_KEY}
bucket: ""
region: ""
path-style: true
cdn-domain: <your-cdn-domain-test>
staging:
endpoint: <your-s3-endpoint-staging>
access-key: ${SAIL_STAGING_ACCESS_KEY}
secret-key: ${SAIL_STAGING_SECRET_KEY}
bucket: ""
region: ""
path-style: true
cdn-domain: <your-cdn-domain-staging>
cdn-domain 说明
cdn-domain 用于 sail url 命令生成文件的公开访问地址,填入你的存储服务对应的 CDN 域名即可。
注意:只有 public-read 权限的 bucket 的文件才能通过 CDN 域名访问;私有 bucket 只能通过鉴权的 GetObject 访问。
region 与 path-style 说明
这两个是 S3 协议的通用参数,根据你接入的存储服务选择:
| 参数 |
含义 |
AWS S3 |
MinIO / 自建 |
阿里云 OSS |
region |
数据中心区域 |
填实际值如 us-east-1 |
留空 |
填如 oss-cn-hangzhou |
path-style |
URL 寻址方式 |
false(virtual-hosted) |
true |
false |
- path-style:
true 时 URL 为 endpoint/bucket/key;false 时 URL 为 bucket.endpoint/key。自建 S3 兼容服务通常只支持 path-style。
- region:自建服务通常留空。AWS SDK 内部规则要求 region 非空,留空时代码自动用
us-east-1 占位(不影响实际请求目标,因 endpoint 已被覆盖)。
密钥安全
配置文件中用 ${VAR} 引用环境变量,避免明文存储:
export SAIL_ACCESS_KEY="your-access-key"
export SAIL_SECRET_KEY="your-secret-key"
环境变量覆盖
| 变量 |
作用 |
SAIL_PROFILE |
选择 profile |
SAIL_ENDPOINT |
覆盖 endpoint |
SAIL_ACCESS_KEY |
覆盖 access key |
SAIL_SECRET_KEY |
覆盖 secret key |
SAIL_BUCKET |
覆盖默认 bucket |
SAIL_CDN_DOMAIN |
覆盖 CDN 域名 |
使用
路径语法:s3://bucket/key 显式指定 bucket;s3:///key(空 bucket 段)用配置的默认 bucket;s3://bucket 仅 ls 列桶。跨桶同步仍用显式 s3://bucket/key。
# 查看版本
sail --version # 或 sail -v
# 复制(本地↔s3、s3↔s3);upload/download 为 cp 的别名
sail cp local.txt s3://mybucket/path/local.txt
sail cp local.txt s3:///path/local.txt # s3:/// 用配置默认 bucket
sail upload local.txt # 1 参:上传到默认 bucket,key 用文件名
sail cp -r ./dir s3://mybucket/prefix/ # 递归镜像目录
cat file | sail upload - s3://mybucket/key # 管道输入
# 下载(s3→本地);download 为 cp 的别名
sail cp s3://mybucket/key local.txt
sail download s3://mybucket/key # 1 参:下载到当前目录
# 列举
sail ls s3://mybucket/prefix/
sail ls -l s3://mybucket/ # 长格式:大小+修改时间
sail ls -d s3://mybucket/prefix/ # 只列该层子目录(不含文件),对齐 ls -d
# 树形查看(S3 前缀或本地目录)
sail tree s3://mybucket/prefix/ # 完整树
sail tree -L 2 s3://mybucket/prefix/ # 限深度 2
sail tree -d s3://mybucket/prefix/ # 只显目录
sail tree -s --human s3://mybucket/prefix/ # 文件附人类可读大小
sail tree ./cmd # 本地目录树
# 删除
sail rm s3://mybucket/key
sail rm -r s3://mybucket/prefix/
# 预签名 URL(部分服务不支持,见下方"限制")
sail presign s3://mybucket/key --expires 3600
# 生成 CDN 访问地址
sail url s3://mybucket/path/file.jpg
sail url s3://mybucket/path/file.jpg --cdn https://<your-cdn-domain>
# 查看对象/文件内容(按格式智能渲染,本地文件免配置)
sail view s3://mybucket/config.json # JSON 自动美化缩进
sail view ./local.log # 文本/代码直出
sail view s3://mybucket/data.csv # CSV 表格对齐
sail view s3://mybucket/photo.png # 图片终端字符画(半块字符,任意终端可见)
sail view s3://mybucket/data.json --raw # 原样输出,适合管道:sail view ... --raw | jq .
sail cat s3://mybucket/data.json # cat 是 view --raw 的别名
sail view s3://mybucket/big.json --force # 跳过大小限制
sail view s3://mybucket/photo.png --width 60 # 指定字符画列宽
# 复制对象/文件(本地↔s3、s3↔s3 走服务端 CopyObject 零带宽)
sail cp ./local.txt s3://mybucket/path/copied.txt
sail cp ./local.txt s3://mybucket/path/ # 尾 / 表示进目录
sail cp s3://mybucket/a.txt ./out.txt
sail cp -r ./dir s3://mybucket/mirror/ # 递归镜像本地目录
sail cp -r s3://mybucket/prefix/ s3://mybucket/dest/ # 服务端递归复制
sail cp --dry-run ./local.txt s3://mybucket/x # 预演,不实际复制
# 移动对象/文件(复制后删除源)
sail mv s3://mybucket/a.txt s3://mybucket/moved.txt # 单对象,无确认
sail mv ./local.txt s3://mybucket/uploaded.txt
sail mv -r s3://mybucket/src/ s3://mybucket/dst/ # 递归,交互确认 [y/N]
sail mv -r --yes s3://mybucket/src/ s3://mybucket/dst/ # 跳过确认
# 查看对象/文件元信息(HeadObject / os.Stat)
sail stat s3://mybucket/config.json # size/content-type/last-modified/etag
sail stat ./local.log # 本地文件元信息
# 切换 profile
sail -p test upload local.txt s3://testbucket/local.txt
与 AWS CLI 对照验证
行为与 aws s3 一致,可用 AWS CLI 对照:
aws s3 ls --endpoint-url <your-s3-endpoint> s3://mybucket/
限制
- Bucket 与 Object key 的命名规则、长度上限取决于所接入的 S3 服务,遵循各服务约束。
- 部分 S3 兼容服务不支持预签名 URL:某些自建 S3 服务不支持 query string 认证(返回 "Authorization empty"),只支持 Authorization header 认证。如需公开访问,请通过 CDN 域名访问已设置为公开的文件。
实现细节
S3 兼容性适配
部分自建 S3 兼容服务与标准 AWS S3 存在差异,工具已做适配:
- checksum 禁用:AWS SDK v2 默认在上传时使用
aws-chunked content encoding + CRC32 trailing checksum。部分 S3 兼容服务端不解码 aws-chunked,导致存储的数据被 trailer 污染(大文件 multipart upload 尤其严重)。工具在 client 和 uploader 两处均设置了 RequestChecksumCalculation = WhenRequired 和 ResponseChecksumValidation = WhenRequired 来禁用此行为。
- region 占位:部分 S3 服务 region 为空,但 AWS SDK v2 的 endpoint 规则要求 region 非空。工具用
us-east-1 作为占位值(endpoint 已被 BaseEndpoint 覆盖,实际不影响请求)。
- CopyObject 回退:部分 S3 兼容服务的
CopyObject 返回成功但生成 0 字节对象。cp/mv 的 s3↔s3 路径在 CopyObject 后用 HEAD 校验目标大小与源一致;不一致时自动回退到 download→re-upload,保证数据正确。标准 S3(AWS/MinIO)上 CopyObject 校验通过,仍走零带宽服务端复制。
发布
发布走 GitHub Actions 自动化:推送形如 vX.Y.Z 的 tag 即触发交叉编译 + 发布到 npm,无需本地登录。
- 在仓库 Settings → Secrets and variables → Actions 添加
NPM_TOKEN(npm automation token,需有 @becrafter scope 发布权)。
- 打 tag 并推送:
git tag v0.1.0 && git push origin v0.1.0
- workflow 跑完,4 个平台子包 + 主包即发布到
registry.npmjs.org。也可在 Actions 页面手动触发并填版本号。
本地发布(无 CI 时)仍可用:make release VERSION=0.1.0(未登录会引导 npm login)。
贡献
欢迎提 Issue 或 Pull Request:https://github.com/BeCrafter/sail/pulls
许可证
MIT