我设计了一款新颖的图床
我最近在做一个图床,叫 Picture Cloud。技术栈是 Rust、PostgreSQL、Redis 和 React。
真正让我觉得有意思的,不是用了哪一个框架,而是我把它拆成了两部分:主控负责账户、权限、调度和对外访问;Agent 只负责某一台机器上的文件读写。
这不是产品发布,也不是一篇完整教程。我只是想把做这个项目时觉得有意思的一种思路分享出来。
先说最核心的想法
普通图床通常会把 API 和图片放在同一台服务器上。这样起步很简单,但想增加一个存储位置时,往往就像是在搬整个应用。
Picture Cloud 把这两件事分开。用户接触到的是主控,真正保存文件的是各个 Agent。
flowchart LR U[浏览器 / API 客户端] --> M[Picture Cloud 主控] M --> DB[(PostgreSQL)] M --> R[(Redis)] M <--> A1[官方 Agent] M <--> A2[社区 Agent] A1 --> F1[(本地对象目录)] A2 --> F2[(本地对象目录)]
Agent 主动通过 WSS 连接主控,所以存储节点不需要单独准备公网 IP、域名、证书或入站端口。对外的图片 URL 始终由主控提供,访问者也不需要知道原文件究竟放在哪台机器上。

每一部分分别做什么
-
React:网页界面,包括上传、图库、API Key 和节点管理。
-
Rust + Axum:主控服务,处理认证、图片 API、公共访问和 S3 兼容接口。
-
PostgreSQL:保存用户、图片引用、物理位置和任务状态。
-
Redis:处理短期缓存、限流和计数。
-
Agent:只做存储动作,包括写入、读取、删除、容量预留和健康上报。
这样拆开以后,系统的边界比较清楚:主控决定“应该做什么”,Agent 执行一个范围明确的文件操作。
上传一张图片会发生什么
大致可以看成四步:
-
主控检查用户、配额和请求的 Region。
-
主控让 Agent 预留空间,再通过 WSS 隧道流式转发图片。
-
Agent 先写入临时文件,校验元数据后,再原子提交对象。
-
主控记录图片引用,返回一个稳定的公共 URL。
图片处理和去重放在主控一侧。系统会分别记录原始哈希和处理后哈希,同一张原图再次上传时,可以复用已经处理好的 Blob,不必重复占用空间。

这样做还有一个好处:公共链接和文件实际存放位置没有直接关系。Blob 从一台节点迁移到另一台节点时,文章里已经使用的链接不需要改变。
为什么要有 Region
Region 是 official-1、community-home 这样的逻辑名称,不是 IP 地址。用户可以手动选择 Region,也可以交给调度器,根据节点状态和剩余容量自动选择。
这个间接层在实际使用时很方便。服务器可以更换、迁移或暂时下线,而用户不需要跟着修改一堆地址。

社区节点默认不会直接进入 Auto 调度。它可以先给节点所有者自己使用,或者等管理员审核以后再接收公共图片。这样至少不会让一个刚上线、稳定性还不确定的节点马上承接所有流量。
这个方案的代价
代价也很明显:图片流量会经过主控和隧道。服务规模变大以后,主控的带宽和中继能力就会成为瓶颈。
对现在这种规模不大的公共服务来说,我觉得简单更重要。以后如果继续增长,可以考虑 CDN 缓存、签名直读,或者按区域增加主控,减少所有流量都经过同一个中继。
社区节点也不能承诺和自有服务器一样的持久性。重要图片仍然需要副本策略或独立备份。
我觉得有意思的地方
Picture Cloud 并没有发明一种新的存储协议。它比较特别的地方,是把统一入口、自助接入的存储节点、出站隧道、逻辑 Region 和内容去重组合到了一起。
简单说,就是把“有人愿意提供一点磁盘空间”变成一个不需要额外暴露服务器的节点。这就是我想尝试的方向。
评论0
还没有评论,来说两句吧~