自建图床系统架构

引言

大家好,今天来聊聊我自己搭建图床系统的经历。相信很多做技术的朋友都有过被第三方图床"绑架"的经历——要么突然收费,要么限制流量,更有甚者直接倒闭让你图片全部挂掉。我反正是在某浪图床凉凉的時候丢过一批图片,心痛之余决定自己动手丰衣足食。今天就把我的设计方案分享出来,希望能给想自建图床的朋友们一点参考。

技术选型:简单够用就行

当初选技术栈的时候,我给自己定了几个原则:首先是部署要简单,别搞得太复杂;其次是资源占用不能太高,毕竟我只是个人使用;最后就是维护成本要低,别天天折腾。

后端我选的是 Node.js + Express,原因很简单,写起来顺手,生态也比较丰富。数据库方面用了 SQLite,轻量级够用,数据量不大的情况下完全没问题。如果你图片量特别大,换成 MySQL 或者 PostgreSQL 也行,就是改个连接的事。

存储方面我用的是 本地磁盘 + OSS 的混合模式。平时图片存本地,定期同步到阿里云 OSS 做备份。这样既保证了访问速度,又不怕硬盘挂掉。

前端就很简单了,一个单页面应用,用原生 HTML + CSS + JavaScript 写的,没用什么框架。主要功能就是上传、浏览、复制链接这几样,犯不着搞得太复杂。

系统架构:分层设计思路

整体架构我采用了比较经典的三层结构:接入层、处理层、存储层。

┌─────────────┐     ┌─────────────┐     ┌─────────────┐

│ 接入层 │ ──▶ │ 处理层 │ ──▶ │ 存储层 │

│ (Nginx) │ │ (Node.js) │ │(本地+OSS) │

└─────────────┘ └─────────────┘ └─────────────┘

接入层 用 Nginx 做反向代理,顺便承担静态资源和图片的缓存。Nginx 配置好 gzip 压缩和缓存策略,能减轻不少后端压力。 处理层 就是我们的 Node.js 服务,负责接收上传请求、处理图片、生成缩略图、管理元数据等核心业务逻辑。 存储层 分两部分:原始图片和元数据存本地数据库,备份图片同步到 OSS。后面我会详细说这块的实现。

核心功能实现

上传与处理流程

用户上传图片的流程是这样的:

// 简化的上传处理逻辑

app.post('/upload', async (req, res) => {

// 1. 接收文件

const file = req.file;

// 2. 生成唯一文件名(用 UUID + 时间戳)

const filename = ${uuid()}${extname(file.originalname)};

// 3. 图片处理(压缩、生成缩略图)

await sharp(file.path)

.resize(1920, 1080, { fit: 'inside' })

.jpeg({ quality: 80 })

.toFile(path.join(UPLOAD_DIR, filename));

// 4. 生成缩略图

await sharp(file.path)

.resize(300, 300, { fit: 'cover' })

.jpeg({ quality: 60 })

.toFile(path.join(THUMB_DIR, thumb_${filename}));

// 5. 存入数据库

const image = await Image.create({

filename,

originalName: file.originalname,

size: file.size,

path: /uploads/${filename}

});

// 6. 返回结果

res.json({ success: true, url: /uploads/${filename}, id: image.id };

});

这里用到了 sharp 这个库,处理图片非常方便。支持格式转换、裁剪、压缩等功能,性能也不错。我设置了最大分辨率 1920x1080,JPEG 质量 80%,这样在保证画质的前提下能显著减小文件体积。

存储策略设计

存储这块我花了比较多心思。核心思路是分层存储 + 冷热分离

1. 热数据(最近三个月的图片):存本地 SSD,访问速度最快

2. 温数据(三个月到一年的图片):存普通硬盘

3. 冷数据(一年以上的图片):同步到 OSS 做归档

实现方式是用 crontab 定时任务,每天凌晨执行一次数据迁移:

# 每天凌晨 3 点执行迁移脚本

0 3 * * * /usr/bin/node /var/www/imagebed/scripts/migrate.js

迁移脚本会检查图片的创建时间,根据时间将文件移动到不同目录,并同步更新数据库记录。OSS 那边用的是阿里云的 SDK,定期把冷数据目录的文件上传上去。

URL 设计与访问优化

为了让图片链接好记又好用,我设计了这样的 URL 格式:

https://img.mydomain.com/{hash}.jpg

https://img.mydomain.com/thumb/{hash}.jpg

hash 是根据文件内容生成的 MD5 值,这样相同内容的图片只会存一份,节省空间。Nginx 配置了静态资源缓存,设置合理的 Expires 和 Cache-Control 头:

location /uploads/ {

expires 30d;

add_header Cache-Control "public, immutable";

}

性能优化经验

跑了一段时间后,做了几个优化,效果还挺明显的:

1. 图片压缩:前面提到了,用 sharp 压缩后,图片体积平均减少 60% 左右

2. Nginx 缓存:设置了浏览器缓存和代理缓存,减少回源次数

3. 数据库索引:给常用的查询字段加了索引,比如按时间排序、按 hash 查询等

4. 连接池:Node.js 里用了 generic-pool 管理数据库连接,避免频繁创建销毁

后来又加上了 WebP 支持,根据请求的 Accept 头自动返回对应格式的图片,又能省不少流量。

监控与运维

运维方面我用了 PM2 来管理 Node.js 进程,自动重启、负载监控这些功能都很实用。日志用的是 pino,配合 logrotate 做轮转。

还写了个简单的监控脚本,每天检查几个关键指标:磁盘剩余空间、图片数量、响应时间等。异常情况会发邮件通知我。目前跑了快一年,除了硬盘换过一次,基本没出过问题。

总结

这套图床系统跑下来,整体感觉还是很稳的。成本方面主要就是域名的费用和 OSS 的存储费,一个月加起来不超过二十块钱,性价比很高。

如果你也想自建图床,我建议可以从简单开始:先本地跑起来,确保功能正常;然后加上图片压缩和质量控制;最后再考虑备份和 OSS 同步。不用一开始就想做得多完善,够用就行,后面慢慢迭代。

有问题欢迎评论区交流,咱们下期见!