基于 Cloudflare 部署的多厂商 OSS 文件管理系统,支持 WebDAV 协议。
- 📁 文件管理: 文件上传、下载、预览、移动、重命名、删除
- 🪣 多存储支持: 支持 Cloudflare R2、AWS S3、阿里云 OSS、腾讯云 COS、华为云 OBS、Backblaze B2、MinIO 等
- 📦 Telegram 存储: 通过 Telegram Bot API 存储文件,支持大文件分片上传(最大2GB)
- 🔄 大文件上传: 分片上传、断点续传、秒传
- 🔗 文件分享: 支持文件/文件夹分享,密码保护、过期时间、下载次数限制
- 📤 上传链接: 创建公开上传链接,允许他人无需登录上传文件到指定文件夹
- 📁 文件夹上传: 支持拖拽上传整个文件夹,自动重建目录结构
- 📝 文件预览: 图片、视频、音频、PDF、Office 文档、代码高亮
- 🔐 权限管理: 文件/文件夹级别的权限控制
- 🏷️ 标签系统: 为文件添加自定义标签
- 🔍 高级搜索: 按名称、类型、大小、时间等条件搜索
- 📥 离线下载: 支持 URL 离线下载到云存储
- 📡 WebDAV: 完整的 WebDAV 协议支持(优化 Windows 资源管理器兼容性)
- 🔄 存储桶迁移: 支持在不同存储桶之间迁移文件(跨 provider)
- 💾 文件去重: Copy-on-Write 机制,相同文件只存储一份
- 👥 多用户: 用户管理、存储配额、审计日志
- ⏰ 定时任务: 自动清理回收站、过期分享
| 组件 | 技术 |
|---|---|
| 前端 | React 18 + Vite 5 + Tailwind CSS 3 |
| 后端 | Hono 4 + Cloudflare Workers |
| 数据库 | Cloudflare D1 (SQLite) + Drizzle ORM |
| 存储 | S3 兼容协议 (R2/S3/OSS/COS/OBS/B2/MinIO) + Telegram Bot API |
| 认证 | JWT + bcrypt |
- Node.js >= 20.0.0
- pnpm >= 8.0.0
- Cloudflare 账户
# 克隆项目
git clone https://github.com/your-repo/ossshelf.git
cd ossshelf
# 安装依赖
pnpm install
# 配置环境变量
cp apps/api/wrangler.toml.example apps/api/wrangler.toml
# 编辑 wrangler.toml,填入 D1 数据库 ID 和 KV 命名空间 ID
# 创建数据库
wrangler d1 create ossshelf-db
wrangler kv:namespace create KV
# 运行迁移
pnpm db:migrate:local
# 启动开发服务器
pnpm dev:api # API 服务
pnpm dev:web # 前端服务ossshelf/
├── apps/
│ ├── api/ # 后端 API (Hono + Cloudflare Workers)
│ └── web/ # 前端应用 (React + Vite)
├── packages/
│ └── shared/ # 共享代码 (常量、类型)
└── docs/ # 文档
├── api.md # API 文档
├── architecture.md # 架构文档
└── deployment.md # 部署文档
- 拖拽上传、文件夹上传
- 大文件分片上传(>= 100MB 自动启用)
- 文件预览(图片、视频、音频、PDF、Office、代码)
- 文件夹上传类型限制
- 回收站(30天保留期)
- 文件去重(Copy-on-Write)
- 支持多个存储桶同时配置
- 每个存储桶可独立设置配额
- 支持的存储提供商:
- Cloudflare R2
- AWS S3
- 阿里云 OSS
- 腾讯云 COS
- 华为云 OBS
- Backblaze B2
- MinIO
- 自定义 S3 兼容存储
- Telegram (通过 Bot API)
- 存储桶迁移: 支持在不同存储桶之间迁移文件,支持跨 provider 迁移
通过 Telegram Bot API 存储文件,利用 Telegram 的免费存储资源:
配置方法:
- 创建一个 Telegram Bot(通过 @BotFather)
- 获取 Bot Token
- 创建一个频道或群组,将 Bot 添加为管理员
- 获取 Chat ID(频道/群组/私聊的 ID)
- 在存储桶管理中选择 Telegram 提供商并填入配置
特点:
- 支持自定义 Bot API 代理地址
- 自动根据文件类型选择合适的上传方式
- 支持文件预览和下载
- 静默发送,不打扰聊天
- 大文件分片上传: 超过 49MB 的文件自动分片上传,最大支持 2GB
限制:
- 单文件最大 2GB(分片上传)
- 小文件直传阈值 50MB
- 单分片最大 30MB
- 无法真正删除文件,只能删除消息引用
- 需要稳定的网络连接到 Telegram API
- 公开/私密分享
- 密码保护
- 过期时间设置
- 下载次数限制
- 文件夹分享: 支持浏览文件夹内容,可选择下载单个文件或打包 ZIP 下载全部/部分文件
- 创建公开上传链接,无需登录即可上传
- 支持密码保护、过期时间
- 可设置单文件大小上限
- 可设置允许的文件类型
- 可设置最多上传文件数
- 自动继承目标文件夹的类型限制
- 三级权限:只读、读写、管理
- 文件/文件夹级别授权
- 权限继承
完整支持 WebDAV 协议,可使用任何 WebDAV 客户端连接,特别优化了 Windows 资源管理器兼容性:
| 配置项 | 值 |
|---|---|
| 服务器地址 | https://your-domain.com/dav |
| 用户名 | 注册邮箱 |
| 密码 | 账户密码 |
| 认证方式 | Basic Auth |
Windows 资源管理器优化:
- 修复 401 响应必须携带 DAV 头的问题
- 确保 PROPFIND 响应路径与请求路径精确匹配
- 实现 LOCK/UNLOCK 操作,解决 Windows 写操作卡死问题
支持的操作:PROPFIND、GET、HEAD、PUT、MKCOL、DELETE、MOVE、COPY、LOCK、UNLOCK、PROPPATCH
- 用户管理(查看、编辑、删除)
- 注册开关控制
- 邀请码系统
- 系统统计
- 审计日志
详细的 API 文档请参阅 docs/api.md。
| 路由前缀 | 说明 |
|---|---|
| /api/auth | 用户认证 |
| /api/files | 文件管理 |
| /api/buckets | 存储桶管理 |
| /api/share | 文件分享 |
| /api/presign | 预签名 URL |
| /api/tasks | 上传任务 |
| /api/downloads | 离线下载 |
| /api/batch | 批量操作 |
| /api/search | 文件搜索 |
| /api/permissions | 权限管理 |
| /api/preview | 文件预览 |
| /api/admin | 管理员接口 |
| /api/migrate | 存储桶迁移 |
| /api/telegram | Telegram 存储 |
| /cron | 定时任务 |
| /dav | WebDAV |
详细的部署文档请参阅 docs/deployment.md。
# 创建生产资源
wrangler d1 create ossshelf-db
wrangler kv:namespace create KV --preview false
# 配置 wrangler.toml
# 运行迁移
pnpm db:migrate
# 部署 API
pnpm deploy:api
# 构建并部署前端
pnpm build:web
wrangler pages deploy apps/web/dist --project-name=ossshelf-web| 限制项 | 值 |
|---|---|
| 单文件最大大小 | 5 GB |
| 默认存储配额 | 10 GB |
| 分片上传阈值 | 100 MB |
| 分片大小 | 10 MB |
| 最大并发分片 | 3 |
| JWT 有效期 | 7 天 |
| WebDAV 会话有效期 | 30 天 |
| 回收站保留期 | 30 天 |
| 登录失败锁定次数 | 5 次 |
| 登录锁定时长 | 15 分钟 |
| Telegram 单文件上限 | 2 GB |
| Telegram 分片阈值 | 50 MB |
| Telegram 单分片大小 | 30 MB |
S3 兼容存储(R2、AWS S3、阿里云 OSS、腾讯云 COS、华为云 OBS、Backblaze B2、MinIO 等)采用统一的上传逻辑:
| 常量 | 值 | 说明 |
|---|---|---|
MULTIPART_THRESHOLD |
100 MB | 分片上传阈值 |
UPLOAD_CHUNK_SIZE |
10 MB | 分片大小 |
MAX_FILE_SIZE |
5 GB | 单文件最大限制 |
┌─────────────────────────────────────────────────────────────────┐
│ 文件上传请求 │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────┐
│ 文件大小判断 │
└─────────────────┘
│
┌───────────────┴───────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ ≤ 100 MB │ │ > 100 MB │
│ 小文件模式 │ │ 分片上传模式 │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 1. POST /create │ │ 1. POST /create │
│ 获取预签名URL │ │ 创建分片任务 │
│ │ │ uploadId │
│ 2. PUT 直传 │ │ │
│ 到 S3 │ │ 2. 循环上传分片 │
│ │ │ POST /part │
│ 3. POST /complete│ │ 获取预签名URL │
│ 完成任务 │ │ PUT 分片到S3 │
│ │ │ │
│ │ │ 3. POST /complete│
│ │ │ 合并分片 │
└─────────────────┘ └─────────────────┘
小文件上传(≤ 100 MB):
POST /api/tasks/create→ 返回{ uploadUrl, taskId, isSmallFile: true }PUT {uploadUrl}→ 直接上传文件到 S3POST /api/tasks/complete→ 完成任务,写入文件记录
大文件分片上传(> 100 MB):
POST /api/tasks/create→ 返回{ uploadId, taskId, totalParts, firstPartUrl }- 循环调用:
GET /api/tasks/part?taskId=&partNumber=→ 获取分片预签名 URLPUT {partUrl}→ 上传分片到 S3POST /api/tasks/part-done→ 记录分片完成
POST /api/tasks/complete→ 合并分片,写入文件记录
Telegram 存储通过 Bot API 实现,采用与 S3 类似但独立的分片逻辑:
| 常量 | 值 | 说明 |
|---|---|---|
TG_CHUNK_THRESHOLD |
50 MB | 分片上传阈值 |
TG_CHUNK_SIZE |
30 MB | 分片大小 |
TG_MAX_FILE_SIZE |
50 MB | 单文件直传上限(Bot API 限制) |
TG_MAX_CHUNKED_FILE_SIZE |
2 GB | 分片上传文件上限 |
┌─────────────────────────────────────────────────────────────────┐
│ 文件上传请求 │
│ (bucketId 指向 Telegram 桶) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────┐
│ 文件大小判断 │
└─────────────────┘
│
┌───────────────┴───────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ ≤ 50 MB │ │ > 50 MB │
│ 小文件模式 │ │ 分片上传模式 │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ uploadId │ │ uploadId │
│ = 'telegram' │ │ = 'telegram- │
│ │ │ chunked:xxx' │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ 1. POST /create │ │ 1. POST /create │
│ isSmallFile │ │ totalParts │
│ = true │ │ = ceil(size │
│ │ │ / 30MB) │
│ 2. POST │ │ │
│ /telegram- │ │ 2. 循环上传分片 │
│ part │ │ POST │
│ (整个文件) │ │ /telegram- │
│ │ │ part │
│ 3. POST /complete│ │ (每片≤30MB) │
│ 写入文件记录 │ │ │
│ + TG引用 │ │ 3. POST /complete│
│ │ │ 写入文件记录 │
│ │ │ + TG引用 │
│ │ │ + 分片记录 │
└─────────────────┘ └─────────────────┘
小文件上传(≤ 50 MB):
POST /api/tasks/create→ 返回{ taskId, uploadId: 'telegram', isSmallFile: true, isTelegramUpload: true }POST /api/tasks/telegram-part(multipart/form-data)- 字段:
taskId,partNumber=1,chunk=文件 - 直接上传整个文件到 Telegram
- 字段:
POST /api/tasks/complete→ 写入files表 +telegramFileRefs表
大文件分片上传(> 50 MB):
POST /api/tasks/create→ 返回{ taskId, uploadId: 'telegram-chunked:xxx', totalParts, isTelegramUpload: true }- 循环调用
POST /api/tasks/telegram-part(multipart/form-data):- 字段:
taskId,partNumber,chunk=分片 - 每个分片独立上传到 Telegram
- 分片记录写入
telegramFileChunks表
- 字段:
POST /api/tasks/complete→ 校验分片完整性,写入文件记录
| 表名 | 说明 |
|---|---|
files |
文件元数据(所有存储类型共用) |
telegramFileRefs |
Telegram 文件引用(tgFileId 映射) |
telegramFileChunks |
Telegram 分片记录(大文件分片信息) |
下载时根据 tgFileId 前缀判断:
- 普通
tgFileId:直接调用 Telegram API 下载 chunked:xxx前缀:从telegramFileChunks读取分片列表,按顺序下载并合并
前端 presignUpload.ts 根据后端返回的标志选择上传路径:
// 判断是否需要分片
if (file.size > MULTIPART_THRESHOLD) {
// S3 分片上传
return multipartUpload({ ... });
}
// 小文件上传
const init = await apiPost('/api/tasks/create', { ... });
if (init.isTelegramUpload) {
// Telegram 上传(小文件或分片)
return telegramProxyUpload({ ... });
}
if (init.uploadUrl) {
// S3 直传
await directPut(init.uploadUrl, file, ...);
}| 特性 | S3 兼容存储 | Telegram 存储 |
|---|---|---|
| 分片阈值 | 100 MB | 50 MB |
| 分片大小 | 10 MB | 30 MB |
| 单文件上限 | 5 GB | 2 GB |
| 直传方式 | 预签名 URL | 代理上传 |
| 分片存储 | S3 服务端合并 | 独立消息 + 虚拟合并 |
| 删除支持 | 完整删除 | 仅删除引用 |
# 开发
pnpm dev:web # 启动前端开发服务器
pnpm dev:api # 启动 API 开发服务器
# 构建
pnpm build:web # 构建前端
pnpm build:api # 构建 API
# 部署
pnpm deploy:api # 部署 API 到 Cloudflare Workers
# 数据库
pnpm db:generate # 生成数据库迁移
pnpm db:migrate # 运行数据库迁移
pnpm db:studio # 打开 Drizzle Studio
# 代码质量
pnpm lint # 运行 ESLint
pnpm lint:fix # 自动修复 ESLint 问题
pnpm format # 格式化代码
pnpm typecheck # 类型检查MIT