全部文章

S3 对象存储

S3 兼容存储使用、Cloudflare R2、MinIO 自建、rclone 同步与 SDK 集成

目录 16 节

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

推荐接入顺序

建议按下面顺序搭:

  1. 先创建 Bucket
  2. 配置最小权限 Access Key
  3. 用 CLI 或控制台先上传一个测试文件
  4. 再接入应用代码和 SDK
  5. 最后再补公开访问、签名 URL、生命周期和同步策略

不要一上来就把应用直接绑到生产 Bucket,先确认权限、路径和内容类型都正确。

安全与管理建议

  • Access Key 不要写死进仓库
  • 公开读和私有读写要分 Bucket 或分路径策略
  • 上传时记得带 Content-Type
  • 对用户上传内容,最好单独隔离并限制访问方式

如果对象存储会承载用户上传文件,建议尽早考虑:

  • 文件命名策略
  • 覆盖冲突
  • 生命周期清理
  • 防止任意公开访问

常见问题

上传成功了,但浏览器打开不对

高频原因通常包括:

  • Content-Type 没设置
  • 对象权限或公开访问策略不对
  • CDN 缓存了旧内容

本地 SDK 能连,线上不能连

优先检查:

  • Endpoint 是否正确
  • 环境变量是否加载成功
  • Access Key / Secret Key 是否有权限
  • 部署环境是否能访问对象存储地址

想把它当网盘一样挂载

这时 rclone mount 很有用,但它更适合轻量访问和同步,不一定适合所有高频随机读写场景。

延伸阅读

参考链接

阅读建议
  • - 先读标题和摘要,再结合目录决定从哪个章节开始精读。
  • - 看到具体命令、配置或步骤时,尽量在自己的环境里同步验证。
  • - 如果你只是快速查资料,可先看目录和相关文档,再决定是否深入全文。
适合谁看
  • - 希望把零散经验整理成长期可复用工作流的人
  • - 想先建立认知,再决定是否深入实践的人
  • - 希望阅读时顺手建立自己的操作清单或收藏体系的人
执行前检查
  • - 先浏览标题、摘要和目录,带着问题阅读会更高效
  • - 顺手记录真正对你有用的命令、链接和注意事项,避免重复搜索
  • - 如果页面里提到相关文档,尽量一起打开对照,效果通常更完整
同类内容
← 上一篇Redis 使用指南