
图床解决的是博客、文档和论坛发帖时的图片托管问题:上传后拿到一个稳定链接,随处引用。本文完成一个自建图床的部署,并把底层存储对接到对象存储,让容量不受服务器磁盘限制。内容涵盖方案选择、部署配置、存储对接、访问加速、防盗链与容量控制,以及图片外链场景特有的成本和合规注意事项。
自建图床有两种存储方式,差别很实际。
存在服务器本地磁盘:配置最简单,读写延迟低。但问题很快会出现——图片只增不减,磁盘迟早写满;图片外链会持续消耗服务器带宽,图片较多的文章一次访问就要传输几 MB;服务器故障时图片全部失效,已发布的文章会大面积显示图片破损。
存到对象存储:图床程序只负责上传和管理,图片实体存在对象存储中。容量按实际用量计费不必预先规划;对象存储提供较高的数据持久性;配合内容分发网络分发时,图片流量不经过服务器,源站带宽压力大幅下降。
对图床这个场景,对接对象存储几乎是必选项。图片外链的流量特征是持续、分散、体积大,正是对象存储加内容分发网络最擅长的负载,用服务器带宽承接既贵又慢。
图床类工具有多种,选择时主要看使用方式:
方案类型 | 特点 | 适合场景 |
|---|---|---|
独立图床程序 | 提供网页上传界面、相册管理、多用户 | 需要管理界面和多人使用 |
客户端直传工具 | 桌面工具直接上传到对象存储,无需服务端 | 只有自己使用,追求简单 |
网盘程序的分享功能 | 复用已部署的网盘 | 已有网盘,不想再多部署一个服务 |
如果只是自己写博客用,客户端直传工具其实最省事——不需要服务器,配好密钥直接传到对象存储。但它缺少统一的图片管理界面,也不方便多人使用。
本文按独立图床程序部署,因为它覆盖的场景更完整。
资源规划:
项目 | 最低配置 | 说明 |
|---|---|---|
CPU | 1 核 | 图片处理时短时占用较高 |
内存 | 1~2 GB | 缩略图生成需要一定内存 |
磁盘 | 20 GB | 接了对象存储后本地只存程序和数据库 |
接了对象存储之后,服务器磁盘需求很小,图片都不落本地。这也是这个方案的优势之一。
需要放通的端口:
端口 | 协议 | 用途 |
|---|---|---|
22 | TCP | SSH 登录(默认已放通) |
80 | TCP | HTTP,用于证书验证与跳转 |
443 | TCP | HTTPS 访问 |
在对象存储控制台创建存储桶,记录以下信息:
信息项 | 说明 |
|---|---|
存储桶名称 | 创建时指定 |
所属地域 | 建议与服务器同地域,上传走内网更快 |
访问域名 | 图片外链会用到 |
密钥 ID 与密钥 Key | 程序访问授权用 |
权限设置需要区分对待,这一点与网盘场景不同。
图床的图片需要被公开访问——文章里的图片链接要能被任何读者打开。所以存储桶或图片所在路径需要允许公共读取。
但要注意:只开放读取,不要开放写入。 公有读写权限意味着任何人都能往你的存储桶上传或删除文件,会被用来存放违规内容或恶意占用你的空间和流量。正确配置是公有读、私有写,上传通过程序用密钥完成。
密钥使用子账号并限定权限。 为图床单独创建子账号,只授予该存储桶的读写权限。不要用主账号密钥——图床程序如果存在漏洞,主账号密钥泄露会影响所有云资源。
以 Docker Compose 方式部署:
mkdir -p ~/apps/imagehost && cd ~/apps/imagehost编写 compose.yaml:
services:
db:
image: mariadb:10.11
environment:
MYSQL_ROOT_PASSWORD: 替换为强密码
MYSQL_DATABASE: imagehost
MYSQL_USER: imagehost
MYSQL_PASSWORD: 替换为强密码
volumes:
- db-data:/var/lib/mysql
restart: unless-stopped
app:
image: 图床程序镜像:固定版本号
ports:
- "127.0.0.1:8080:80"
volumes:
- app-data:/var/www/html
environment:
DB_HOST: db
DB_DATABASE: imagehost
DB_USERNAME: imagehost
DB_PASSWORD: 与上面保持一致
TZ: Asia/Shanghai
depends_on:
- db
restart: unless-stopped
volumes:
db-data:
app-data:几处配置的说明:
DB_HOST: db 用容器服务名。填 localhost 连不上,因为在容器内这个地址指向容器自身。127.0.0.1,必须经过反向代理访问。具体的镜像名称和环境变量以所选程序的官方文档为准,不同程序的配置项差异较大。
启动服务:
docker compose up -d
docker compose ps
docker compose logs app --tail 30在域名服务商处添加 A 记录,解析生效后验证:
dig +short img.example.com配置反向代理,以 Caddy 为例:
img.example.com {
reverse_proxy 127.0.0.1:8080
request_body {
max_size 100MB
}
}request_body max_size 要覆盖单张图片的最大体积。默认限制偏小时,上传较大的图片会失败并返回 413,而界面上往往只显示上传失败。
HTTPS 在图床场景有个额外的必要性:如果你的博客是 HTTPS 站点,而图片链接是 HTTP,浏览器会拦截这些图片并提示混合内容,图片无法显示。所以图床必须启用 HTTPS。
服务器位于中国内地且使用域名对外提供服务时,需要先完成 ICP 备案。图片外链使用的对象存储访问域名如果绑定了自定义域名,同样需要备案。
在图床程序的后台设置中配置存储方式,选择 S3 兼容协议或对象存储对应的选项,填入:
配置项 | 填写内容 |
|---|---|
存储桶名称 | 创建的存储桶名 |
地域 | 存储桶所属地域标识 |
访问域名 | 存储桶的访问地址 |
密钥 ID | 子账号的密钥 ID |
密钥 Key | 子账号的密钥 Key |
存储路径前缀 | 可选,例如 |
具体的参数名称与取值格式请以对象存储的 S3 兼容协议文档为准,不同服务的地域标识和域名格式存在差异。
配置存储路径前缀是个好习惯。 把图床的文件都放在一个固定前缀下,便于后续做权限控制、生命周期管理和统计,也避免与存储桶中其他用途的文件混在一起。
配置完成后上传一张测试图片,然后到对象存储控制台查看存储桶,确认对应路径下出现了新文件。
图片是典型的静态资源,适合用内容分发网络分发。接入后带来两个改善:不同地区访客的加载速度提升;图片流量由分发节点承担,回源流量大幅减少。
在内容分发网络控制台添加域名,源站指向对象存储的访问地址。配置要点:
配置完成后,把图床程序中的图片访问域名改为分发域名,新上传的图片会使用加速链接。
这里有个成本上的注意点:内容分发网络按流量计费。如果图片被大量引用或被恶意刷流量,费用可能超出预期。建议:
防盗链是图床必须配置的一项,否则你的图片可能被其他网站直接引用,流量费用由你承担。
在内容分发网络或对象存储的控制台配置来源限制:
多数个人博客场景建议允许空来源,同时限制域名白名单,在便利和防护之间取平衡。
网页端可通过 HTTPS 访问
curl -I https://img.example.com上传功能正常:在网页端上传一张图片,确认上传成功并返回链接。
图片确实存到了对象存储:在对象存储控制台确认存储桶对应路径下出现了新文件。这一步验证存储对接真的生效,而不是文件仍在写本地磁盘。
外链可正常访问:复制返回的图片链接,在新的浏览器窗口打开,确认图片正常显示。
在实际引用场景中验证:把图片链接插入到你的博客文章中,确认显示正常且没有混合内容告警。这是图床的真实使用场景,必须实测。
大图上传通过:上传一张接近大小上限的图片。小图测试完全不会暴露请求体大小限制的问题。
加速链接生效:如果配置了内容分发网络,确认图片链接使用的是分发域名,并检查响应头中的缓存命中标识。
防盗链生效:这一项建议主动测试。用一个不在白名单中的来源引用图片,确认被拒绝。配了防盗链但实际没生效的情况不少见。
上传后图片无法显示
先确认文件是否真的到了对象存储。到了但访问不了,通常是存储桶或路径的读取权限未开放。检查权限配置,确认允许公共读取。
上传大图失败
检查两处:反向代理的请求体大小限制、图床程序自身的上传大小配置。取值较小的那一处决定实际上限。
图片在博客中显示破损
多为混合内容问题。确认图片链接是 https 开头。如果历史文章中有 http 链接,需要批量替换。
上传成功但链接是内网地址
图床程序中配置的访问域名有误。检查存储配置中的访问域名,应填写公网可访问的地址。
对象存储返回权限错误
检查密钥是否正确、子账号是否有该存储桶的写入权限、存储桶名称和地域是否匹配。
流量费用异常增长
先查看是否被盗链。在对象存储或内容分发网络的日志中分析来源分布,如果有大量来自陌生域名的请求,说明防盗链未生效或配置有误。
也要检查是否有超大文件被反复请求。可以对单文件大小设限,并考虑对图片做压缩处理。
缩略图生成失败
图片处理需要一定内存,处理超大图片时可能超出限制。检查程序日志中的具体报错,必要时提高内存上限或限制上传图片的最大尺寸。
图床的成本主要来自存储和流量,有几个实用的控制手段。
上传前压缩
这是性价比最高的优化。网页展示用的图片通常不需要原图质量,压缩后体积可以显著下降,存储和流量成本同步降低。可以在图床程序中开启自动压缩,或上传前手动处理。
限制单文件大小与格式
设置合理的上限,避免有人上传超大文件。同时限制允许的格式,只接受常见图片格式,拒绝其他类型的文件——这也是一项安全措施,防止图床被用来托管可执行文件。
使用生命周期规则
对象存储支持生命周期管理,可以把较早的图片自动转为成本更低的存储类型。适合归档不常访问的历史图片。
定期清理无用图片
上传了但没有被引用的图片会持续占用空间。部分图床程序提供未引用图片的统计功能,可以定期清理。
设置费用告警
在控制台配置流量和存储的用量告警,异常增长时能及时发现。这比月底看账单要好得多。
这一节对图床格外重要,因为图床是一个对外提供上传能力的服务。
关闭开放注册
如果图床程序支持多用户,务必关闭开放注册。一个允许任意注册的图床很快会被用来托管垃圾内容或违规图片,而你作为服务的运营者要对其上的内容承担责任。
需要给他人使用时,通过后台手动创建账号。
限制上传格式
只允许图片格式。允许任意文件上传会带来严重风险:可能被上传恶意脚本,或被用作文件分发渠道。
内容责任
自建图床意味着你要对存储在上面的内容负责。要点:
保护凭据
对象存储密钥是核心凭据,泄露后他人可以任意读写你的存储桶。不要把密钥写在前端代码或提交到代码仓库中。使用子账号并限定最小权限,定期轮换。
备份要点
图床需要备份的是数据库(记录图片与用户的对应关系)。图片实体在对象存储中,其持久性由对象存储保障。
但要注意:数据库丢失会导致所有图片失去索引——文件还在,但不知道哪张图属于谁、原始文件名是什么。所以数据库备份不能省。
docker compose exec -T db \
mysqldump -u imagehost -p'密码' imagehost | gzip > ~/img-db-$(date +%Y%m%d).sql.gz把备份同步到服务器之外的位置。
变更前创建快照:升级程序版本前给实例创建快照。回滚会把整块系统盘恢复到快照时间点,之后写入的数据会被清除,运行中的实例会自动关机。使用存储型套餐的实例不支持创建快照。
部署完成后,如果需要把其他自建服务的存储也统一到对象存储,或为多个服务配置统一的域名入口,可以作为下一步方向。
图片存储和外链场景,对象存储 COS 提供高持久性、按量计费和生命周期管理,配合CDN分发可显著降低源站带宽压力;运行图床程序的机器,轻量应用服务器的入门规格即可承载。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。