Stash your files like an otter
基于 Cloudflare KV + Telegram Bot API 的免费私人云盘
现有基于 Cloudflare + Telegram 的文件存储方案,例如:
它们都很优秀,但要么偏向图床与轻量分享,要么为了通用性引入了较高的复杂度,并不完全适合长期自用的私人云盘。
像水獭一样,把文件悄悄藏好,需要时再拿出来 🦦
OtterHub 是一个 为个人使用场景定制 的私人云盘方案:
它不追求"什么都支持",而是专注于刚好够用、稳定、好维护。
[!IMPORTANT] 体验站点:OtterHub Demo
账号:
OtterHub| 密码:123456限制:演示站的默认文件不可删,仅支持上传 ≤20MB 文件(1 小时自动清理)

img: audio: video: doc:,提升查询效率sendPhoto 还是 sendDocument,都会优先复用较小预览图做分析,超大原图会安全跳过安装依赖
bash
# 在根目录运行,自动安装所有 Workspaces 依赖
npm install
启动项目
bash
npm run dev
第一次启动需要构建前端
npm run build(生成frontend/out供 Wrangler Pages Dev 使用),后续启动直接npm run dev即可。
访问网站
http://localhost:3000http://localhost:8080 (由 Wrangler 代理)[!TIP] 开发环境下密码为
123456,且采用本地 R2 存储,可以直接上传文件,方便调试。 修改 functions 代码后,可运行npm run ci-test快速测试文件上传和下载功能是否正常。
Fork 本项目,然后在 Cloudflare Dashboard 创建 Pages 项目:
npm install && npm run buildfrontend/out在 Pages 项目的设置中添加以下环境变量:
PASSWORD=your_password # 密码
TG_CHAT_ID=your_tg_chat_id # Telegram Chat ID
TG_BOT_TOKEN=your_tg_bot_token # Telegram Bot Token
API_TOKEN=your_api_token # (可选) 用于 API 调用的 Token
TG_CHAT_ID和TG_BOT_TOKEN需在 Telegram 中获取。 💡 详细流程可参考:Telegraph-Image
oh_file_urloh_file_url 绑定到 Pages 项目,变量名也设为 oh_file_url如需启用图片自动分析功能:
AI,选择默认 Workers AI 资源回到部署页面重试部署,让环境变量和 KV 绑定生效。
以大文件分片上传流程为例
GET /upload/chunk 请求后端创建最终 KV,返回唯一文件 key
分片上传
前端将文件分片(每片 ≤ 20MB)
POST /upload/chunk后端将分片暂存到临时 KV(TTL = 1 小时,value ≤ 25MB)
异步上传到 Telegram
使用 waitUntil 异步上传分片到 Telegram
上传成功后获取 file_id
合并完成
以大文件流式获取流程为例
读取元数据
从 KV 读取文件元数据和分片信息
解析 chunks 数组中的 file_id
流式拉取
从 Telegram API 流式拉取所有分片
边拉取边返回给客户端
断点续传
以 30MB 文件为例
{
"name": "video:chunk_7yHZkP0bzyUN5VLE.mp4",
"metadata": {
"fileName": "示例视频-1080P.mp4",
"fileSize": 30202507,
"uploadedAt": 1768059589484,
"liked": false,
"chunkInfo": {
"total": 2,
"uploadedIndices": [1, 0]
}
}
}
[
{
"idx": 1,
"file_id": "BQACAgUAAyEGAASJIjr1AAIDa2lictGSBOJ24LnypIN5JCmV2u77AAJ_HwAC...",
"size": 9230987
},
{
"idx": 0,
"file_id": "BQACAgUAAyEGAASJIjr1AAIDbGlictIJ9om0qQ66ZW4GssRXCARUAAKAHwAC...",
"size": 20971520
}
]
计算公式:
1GB / 500字节 ≈ 200万
上传过程使用了 waitUntil 进行异步处理,
在分片尚未全部上传完成前,文件可能暂时显示不完整。
通常只需 稍等片刻并刷新页面 即可正常显示。
通过 分片上传 + 流式合并 实现:
file_id👉 当前最大支持 1GB 文件(50 × 20MB)。
对于个人存储场景通常足够,理论存储数量: ≥ 200万个 但大文件上传会占用较多内存和CPU资源,不建议并发上传多个大文件。
具体限制参考官方文档:https://developers.cloudflare.com/workers/platform/limits/
以下为 AI 生成,详细流程可参考:Telegraph-Image
Bot Token
@BotFather/newbotChat ID
- 搜索 @userinfobot 并发送任意消息
- 或访问:
https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates
OtterHub/
├── frontend/ # Next.js Frontend
│ ├── lib/
│ │ ├── api/ # Hono RPC Client (Type-safe)
│ │ │ ├── client.ts # RPC Client Instance
│ │ │ └── ...
│ └── ...
├── functions/ # Cloudflare Pages Functions (Hono Backend)
│ ├── routes/ # 业务路由模块
│ │ ├── file/ # 文件操作
│ │ ├── upload/ # 上传逻辑
│ │ ├── wallpaper/ # 壁纸服务
│ │ └── ...
│ ├── middleware/ # 中间件 (Auth, CORS)
│ ├── utils/ # 工具库
│ │ ├── db-adapter/ # 存储适配器 (Telegram/R2)
│ │ ├── proxy/ # 代理
│ │ └── ...
│ ├── app.ts # Hono App 定义 & AppType 导出
│ └── [[path]].ts # Pages Functions 入口
├── shared/ # 前后端共享类型/工具 (Workspaces)
├── test/ # 基础端到端测试 (mocha)
├── public/ # 静态资源
├── package.json # Monorepo 配置
├── wrangler.jsonc # Wrangler 配置(本地开发/静态资源)
└── README.md
[x] 回收站功能(支持恢复 / 永久删除 / 自动清理)
[x] 文件管理
[x] 临时分享文件(无论是否 Private 都可以访问)
shared:<uuid><file_key>[x] 预览与展示
[x] 图片加载策略(默认 / 省流 / 无图)
[x] 安全与体验
API_TOKEN 环境变量配置)[ ] 申请 TG API ID,自建 Telegram Bot API Server, 单个文件下载上限可提升至 2GB
[ ] KV vs D1 数据库评估
欢迎提交 Issue 反馈问题或建议新功能,也欢迎 Pull Request 一起完善项目! 觉得有用的话,点个 ⭐️ 支持一下吧!
$ claude mcp add OtterHub \
-- python -m otcore.mcp_server <graph>