公众号旧文
一步步搭建一个属于自己的 Go 公用图床
对大家来说,图床并不陌生。
市面上虽然有很多现成的图床。
但我仍然决定自己写一个。
整个项目完全基于 Go 标准库开发。
从一个只有几十行代码的小程序,一步一步演进到了 v0.6。
本文记录整个过程。
第一阶段:项目初始化
首先初始化 Go 项目。
go mod init gallery
整个项目最初只有两个文件:
gallery/
├── go.mod
└── main.go
第一版程序只有一个 HTTP Server。
http.ListenAndServe(":8080", nil)
浏览器能打开:
http://127.0.0.1:8080
就算完成了第一步。
第二阶段:首页
随后增加 HTML 页面。
templates/
└── index.html
页面简单到只有:
- 一个标题
- 一个文件选择框
- 一个 Upload 按钮
Go 使用标准库:
html/template
渲染模板。
整个项目没有引入任何前端框架。
第三阶段:实现上传
真正的图床核心,就是上传。
Go 提供:
r.FormFile("image")
可以直接读取 multipart/form-data。
上传逻辑包括:
- 获取文件
- 创建目标文件
- io.Copy 保存
这一阶段,已经能够:
curl -F "image=@test.png" ...
成功上传图片。
第四阶段:随机文件名
直接使用用户上传的文件名存在很多问题:
- 文件名冲突
- 中文乱码
- 特殊字符
- 路径攻击
于是改为:
crypto/rand
生成 16 字节随机数。
例如:
2acfa0fdfdb99986da5c90a239a4ab38.png
优点:
- 基本不会重复
- 保留扩展名
- URL 更简洁
第五阶段:按年月分类
如果所有图片都放到:
uploads/
以后可能就是几万张图片。
因此改为:
uploads/
└── 2026/
└── 07/
代码根据:
time.Now()
自动生成目录。
这样:
- 文件数量均匀
- 查找方便
- 备份简单
第六阶段:限制只能上传图片
HTML 的:
accept="image/*"
没有任何安全意义。
浏览器可以绕过。
真正可靠的是后端。
Go 使用:
http.DetectContentType()
读取前 512 字节判断 MIME。
允许:
- image/png
- image/jpeg
- image/gif
- image/webp
其它全部拒绝。
例如:
text/plain
返回:
{
"success": false,
"message": "unsupported image type"
}
第七阶段:上传大小限制
如果不限制上传大小。
有人上传超大图片。
服务器很容易被拖垮。
因此增加两层保护。
第一层:
Go
http.MaxBytesReader()
第二层:
Nginx
client_max_body_size 5M;
最终效果:
Browser
│
▼
Nginx
│
>5MB
│
413
只有合法请求才会进入 Go。
第八阶段:统一 JSON API
最开始返回的是:
Upload success
后来全部改成 JSON。
成功:
{
"success": true,
"url": "https://gallery.cc.cd/uploads/2026/07/example.png",
"filename": "example.png",
"size": 12345,
"content_type": "image/png"
}
失败:
{
"success": false,
"message": "unsupported image type"
}
这样以后:
- 前端
- App
- Python
- Java
都可以直接调用。
第九阶段:代码重构
随着代码越来越多,开始拆分文件。
最终形成:
gallery/
├── config.go
├── main.go
├── response.go
├── upload.go
└── util.go
检测图片:
detectImage()
生成文件名:
randomFilename()
保存文件:
saveFile()
构建保存路径:
buildSavePath()
每个函数只负责一件事情,后续维护更加轻松。
第十阶段:Nginx
Go 程序监听:
127.0.0.1:8080
外部无法直接访问。
真正对外的是 Nginx
访问:
gallery.cc.cd
实际上是:
Browser
│
HTTPS
│
Nginx
│
127.0.0.1:8080
│
Gallery
这是非常经典的部署方式。
第十一阶段:HTTPS
使用 Certbot 申请:
gallery.cc.cd
以及:
www.gallery.cc.cd
证书。
随后:
Nginx 自动配置:
- SSL
- HTTP→HTTPS
上传返回的 URL 也改成:
https://gallery.cc.cd/...
目前功能还不算丰富,但基础设施已经非常完整。
照惯例放出源码:
https://github.com/pangzibaike/gallery
欢迎体验:
https://gallery.cc.cd/