S3 对象存储
S3(Simple Storage Service)是对象存储的事实标准,众多服务兼容其 API。
这页适合作为“对象存储选型与接入指南”。如果你是第一次用 S3 兼容存储,最重要的不是先写 SDK,而是先理解 Bucket、Object、Endpoint、Access Key 这些概念分别对应什么。
先按场景选
- 边缘站点和静态资源分发:优先
Cloudflare R2 - 低成本归档与备份:可看
Backblaze B2 - 完全自控和局域网部署:优先
MinIO - 预算敏感、欧洲节点:可看
Hetzner Object Storage
如果你本来就已经跑在 Cloudflare 生态里,R2 的接入体验通常会更顺。
常见 S3 兼容服务
| 服务 | 特点 |
|---|---|
| Cloudflare R2 | 零出口费用 |
| Backblaze B2 | 便宜,兼容 S3 |
| MinIO | 自建,开源 |
| Hetzner Object Storage | 欧洲节点,便宜 |
Cloudflare R2
Wrangler 管理
# 创建 Bucket
npx wrangler r2 bucket create my-bucket
# 列出 Bucket
npx wrangler r2 bucket list
# 上传文件
npx wrangler r2 object put my-bucket/file.txt --file=./file.txt
# 下载
npx wrangler r2 object get my-bucket/file.txt
Workers 中使用
// wrangler.jsonc 绑定
// { "r2_buckets": [{ "binding": "BUCKET", "bucket_name": "my-bucket" }] }
export default {
async fetch(request: Request, env: { BUCKET: R2Bucket }) {
const url = new URL(request.url);
const key = url.pathname.slice(1);
if (request.method === "GET") {
const object = await env.BUCKET.get(key);
if (!object) return new Response("Not Found", { status: 404 });
return new Response(object.body, {
headers: { "Content-Type": object.httpMetadata?.contentType || "" },
});
}
if (request.method === "PUT") {
await env.BUCKET.put(key, request.body);
return new Response("OK");
}
},
};
MinIO 自建
services:
minio:
image: minio/minio:latest
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: changeme123
volumes:
- ./minio-data:/data
command: server /data --console-address ":9001"
访问 http://localhost:9001 进入管理界面。
Node.js SDK
pnpm add @aws-sdk/client-s3
import {
S3Client,
PutObjectCommand,
GetObjectCommand,
} from "@aws-sdk/client-s3";
const s3 = new S3Client({
region: "auto",
endpoint: "https://your-account.r2.cloudflarestorage.com",
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY!,
secretAccessKey: process.env.S3_SECRET_KEY!,
},
});
// 上传
await s3.send(
new PutObjectCommand({
Bucket: "my-bucket",
Key: "images/photo.jpg",
Body: fileBuffer,
ContentType: "image/jpeg",
}),
);
// 下载
const response = await s3.send(
new GetObjectCommand({
Bucket: "my-bucket",
Key: "images/photo.jpg",
}),
);
const data = await response.Body?.transformToByteArray();
rclone 同步
跨存储同步工具。
scoop install rclone
# 配置
rclone config
# 同步
rclone sync ./local-dir remote:bucket-name
rclone copy ./file.txt remote:bucket-name/path/
# 列出文件
rclone ls remote:bucket-name
# 挂载为本地磁盘
rclone mount remote:bucket-name X: --vfs-cache-mode full
推荐接入顺序
建议按下面顺序搭:
- 先创建 Bucket
- 配置最小权限 Access Key
- 用 CLI 或控制台先上传一个测试文件
- 再接入应用代码和 SDK
- 最后再补公开访问、签名 URL、生命周期和同步策略
不要一上来就把应用直接绑到生产 Bucket,先确认权限、路径和内容类型都正确。
安全与管理建议
- Access Key 不要写死进仓库
- 公开读和私有读写要分 Bucket 或分路径策略
- 上传时记得带
Content-Type - 对用户上传内容,最好单独隔离并限制访问方式
如果对象存储会承载用户上传文件,建议尽早考虑:
- 文件命名策略
- 覆盖冲突
- 生命周期清理
- 防止任意公开访问
常见问题
上传成功了,但浏览器打开不对
高频原因通常包括:
Content-Type没设置- 对象权限或公开访问策略不对
- CDN 缓存了旧内容
本地 SDK 能连,线上不能连
优先检查:
- Endpoint 是否正确
- 环境变量是否加载成功
- Access Key / Secret Key 是否有权限
- 部署环境是否能访问对象存储地址
想把它当网盘一样挂载
这时 rclone mount 很有用,但它更适合轻量访问和同步,不一定适合所有高频随机读写场景。
延伸阅读
参考链接
- Cloudflare R2 — 文档
- MinIO — 文档
- rclone — 多云同步
- AWS S3 SDK — JS SDK