ReboFiles文档
开始3
什么是 ReboFiles
快速开始
可运行的演示
集成到你的应用6
React 组件
Headless:你自己的界面
不使用框架
样式与主题
键盘与无障碍
语言
文件9
上传
预览与查看器
搜索
实时更改与大文件夹
评论
星标与字段
版本、回收站与空间
打包下载 .zip
Word 文档
服务器14
路由
框架与登录
谁可以做什么
存储:S3、GCS、Azure 与磁盘
检查上传
元数据:SQLite 与 Postgres
数据库结构
共享与链接
动态与通知
钩子、日志与限制
保留、法律保留与导出
桌面端与其他来源
同步文件夹
上线生产环境
协作1
AI 助手(MCP)
参考5
包
组件与 hooks
客户端 API
HTTP 路由
状态与路线图
↑↓ 移动↵ 打开Esc 关闭38 个页面

服务器 存储:S3、GCS、Azure 与磁盘

存储:S3、GCS、Azure 与磁盘

文件内容从浏览器直接发送到存储桶并从中读回,使用服务器签名、有效期几分钟的地址。这对存储桶有两个要求:一个用于签名的密钥,以及一条允许你的页面访问的规则。
S3

密钥

在 Cloudflare R2 中:R2 → Create bucket,然后 Manage API tokens → Create API token,仅为该存储桶授予 “Object Read & Write”。

.env
# The S3 API of the account, not the public address of the bucket (r2.dev)
S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
S3_BUCKET=acme-files
S3_REGION=auto
S3_ACCESS_KEY_ID=…
S3_SECRET_ACCESS_KEY=…
# Optional: before every key, to share the bucket with something else
S3_PREFIX=files/
# What tokens are signed with: long, random, the same in every process
REBOFILES_SECRET=…

s3Storage(s3OptionsFrom(process.env)) 会读取它们。在 AWS 上,省略 S3_ENDPOINT 并提供区域;MinIO 和 Backblaze 使用相同的变量。

CORS

跨域规则

在 Settings → CORS policy 中,填入你的应用所在的地址。必须暴露 ETag。

CORS policy
[
  {
    "AllowedOrigins": ["https://your-app.com", "http://localhost:3000"],
    "AllowedMethods": ["GET", "PUT"],
    "AllowedHeaders": ["content-type"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3600
  }
]

再加一条生命周期规则,在一天后中止未完成的分片上传,就能避免从未完成的上传分片一直留在存储桶中。

GCS

Google Cloud Storage

通过兼容 S3 的 XML API:同样的地址,用存储桶的 HMAC 密钥签名。

import { gcsOptionsFrom, gcsStorage } from '@rebofiles/server';

// An HMAC key of the bucket: Cloud Storage → Settings → Interoperability.
// GCS_BUCKET, GCS_ACCESS_KEY_ID (GOOG1…), GCS_SECRET_ACCESS_KEY, GCS_PREFIX
createDriveHandler({ ...options, storage: gcsStorage(gcsOptionsFrom(process.env)) });

该 API 不支持的操作换一种方式完成:逐个删除对象;新版本的所有分片都会上传,因为它不支持复制分片。存储桶的 CORS(用 gcloud storage buckets update --cors-file 设置)必须公开 ETag。

Azure

Azure Blob Storage

存储帐户中的一个容器:地址用帐户密钥签名(每个 blob 的 SAS,有效几分钟),每个分片是一个块,文件由块列表组成。

import { azureOptionsFrom, azureStorage } from '@rebofiles/server';

// AZURE_STORAGE_ACCOUNT, AZURE_STORAGE_KEY, AZURE_STORAGE_CONTAINER, AZURE_STORAGE_PREFIX
createDriveHandler({ ...options, storage: azureStorage(azureOptionsFrom(process.env)) });
  • 根据 Azure 列出的块继续中断的上传;新版本只上传变化的分片(Put Block From URL)。
  • 从未完成的上传留下的块会在一周后自动清除:无需清理。
  • 帐户的 CORS(资源共享)需允许你的页面来源,并允许 PUT、GET 和 HEAD;无需公开任何响应头,因为块按编号组合。
  • 已在服务器的测试中用微软的模拟器 Azurite 验证。
磁盘

存放在磁盘上

diskStorage({ folder, secret }) 把文件内容保存在一个文件夹中,由服务器自己通过它的地址传输:适用于单机、演示或测试。

错误

出现问题时

S3_ENDPOINT is the public address of the bucket (r2.dev)

R2 的 S3 API 位于 https://<account-id>.r2.cloudflarestorage.com,可在存储桶的设置页面找到。

The part could not be sent. If the bytes go to a bucket, check that it lets this origin in.

跨域规则问题:AllowedOrigins 中缺少页面地址,或 AllowedHeaders 中缺少 content-type。

The bucket did not let the page read the ETag of a part.

小文件能上传而大文件不能:ExposeHeaders 需要包含 ETag。

The bucket answered 403 to …

密钥问题:它无权写入该存储桶,或者 endpoint、区域或存储桶名称与创建密钥时的不一致。

There is no room for it: … are used, and it is ….

该用户的配额已满(413 over-quota):在网盘设置中增加空间,或减少其文件和回收站中的内容。

在仓库中,npm run storage:check 会针对 apps/storage/.env 中的存储桶走一遍文件的完整流程,并告诉你出错那一步存储桶返回了什么。