Hugo + PicList 多图床搭建实践

Hugo 写作时,Markdown 通过路径或 URL 引用图片,不直接管理图片资源。随着文章和图片增多,可借助 PicList 统一处理上传与链接生成,由图床负责存储访问。
下面以 Typora + PicList + Hugo 为例,介绍一种可在本地与云端灵活切换的图片写作方案,并分别说明本地、GitHub 与 Cloudflare R2 三种低成本图床的配置方式。
准备工作
在开始之前,准备以下工具:
- PicList:用于图床图片的上传与管理,需提前配置好目标图床。
- Typora:Markdown 编辑器,用于编辑与预览文章内容。
- Git(可选):用于 GitHub 图床的同步及博客的版本管理。
- VS Code(可选):用于博客项目管理及 Markdown 内容编辑。
搭建图床
本地图床
本地图床是一种简单且低成本的图床方案,PicList 支持将本地文件夹作为图床目录,提供三种处理方式:
- 预处理:上传图片至本地目录,Hugo 构建时修正路径,适合长期写作和维护。
- 后处理:上传图片生成绝对路径,Hugo 引用外链,配置成本最低,但本地无法预览。
- 手动处理:Typora 将图片保存到指定目录,依赖人工管理,不适合图片数量多的项目。
图片一般存放于 assets 或 static 目录 —— assets 适合需要编译、压缩或转换的资源;static 直接复制到 public 目录,适合存放原始图片。
GitHub 图床
GitHub 图床通过代码仓库存储图片,PicList 自动上传生成外链,并可配合 CDN 加速。
打开 GitHub,点击 +,创建 New Repository:
- Repository name:如
hugo-img。 - Configuration:勾选
Add a README.md,其余保持默认设置 → 点击 Create Repository。
点击头像 > Settings > Developer Settings > Personal access tokens,创建 Generate new token(classic):
- Note:如
Typora-PicList。 - Expiration:
No expiration。 - Select scopes:勾选
repo权限。
生成并保存 Token(关闭页面后无法再次查看)。
Cloudflare R2
Cloudflare R2 是兼容 S3 的对象存储,支持自定义域名和全球 CDN,零流量费,提供一定免费额度,超出部分按需付费。1
以下是必需的操作:
点击 存储和数据库 > R2 对象存储 > 绑定支付方式 > 创建存储桶(如 blog),选择亚太地区。
其中,支付方式支持绑定 Visa 或 PayPal。如果无信用卡,可使用 PayPal 绑定国内储蓄卡完成验证,之后可解除绑定。
R2 存储桶提供的公共访问链接受服务器限制,几乎无法访问,建议绑定自定义域名使用。
购买并实名认证域名 (如 阿里云万网)后,将域名接入 Cloudflare:
- 打开 Cloudflare,点击 添加域,输入域名(如
example.com),选择 Free 套餐,记录其分配的两条名称服务器。 - 打开 阿里云域名控制台,点击 域名列表 > 选择域名 > DNS 修改,将名称服务器替换为 Cloudflare 提供的两条地址。
- 打开 R2 对象存储,选择存储桶,点击 设置,启用 公共开发 URL,并 添加自定义域(如
img.example.com)。
完成后,关闭 公共开发 URL。
API 密钥将用于 PicList 配置中,确保图片能够正常上传。
打开 R2 对象存储,点击 API Tokens > Manage,创建 Account API 令牌:
- 权限:对象读与写。
- 其他设置:保持默认。
创建后,保存 AccessKey ID 和 SecretAccessKey(关闭页面后不可再次查看)。
以下是可选的操作:
通过防盗链限制仅允许站点自身引用图片,避免无效流量消耗。
打开 域,选择域名,点击 安全规则,创建 自定义规则:
- 规则名称:防盗链保护。
- 表达式:
(http.host eq "img.example.com" and not http.referer contains "example.com" and http.referer ne "" and not http.referer contains "http://localhost:1313") - 选择操作:阻止。
其中,img.example.com 为 R2 存储桶自定义子域,example.com 为站点主域名。
边缘缓存启用后,请求将优先由 Cloudflare 全球 CDN 响应,减少回源请求并降低费用。
打开 域,选择域名,点击 规则,创建 页面规则:
- URL:R2 存储桶自定义子域(如
img.example.com/*)。 - 设置:缓存级别 - 缓存所有内容;边缘缓存 TTL - 1 个月;浏览器缓存 TTL - 8 小时。
通过配置 Cache-Control 响应头,进一步控制 CDN 与浏览器缓存行为。
打开 域,选择域名,点击 规则 > 概述,创建 响应标头转换规则:
- 规则名称:图片缓存设置。
- 如果传入请求匹配:自定义筛选表达式 → 主机名等于存储桶自定义子域(如
img.example.com)。 - 则:添加静态 →
Cache-Control=public,max-age=31536000, immutable。
当博客域名与图床域名不一致时,需要配置 CORS 以避免浏览器跨域拦截。
打开 R2 对象存储,选择存储桶,点击 设置 > CORS 策略 > 编辑(记得替换 example.com):
| |
用量通知可用于监控异常访问或流量激增,避免意外费用。
打开 Cloudflare 控制台,点击 管理账户 > 通知 > 添加通知 > 选择账单:
- 通知名称:R2 用量警报。
- 产品:R2 Storage Class B Operations。
- 总数 number of Storage Class B Operations 超过时发出通知:50000。
配置 PicList
打开 PicList,点击 设置 > 上传,建议启用以下选项:
- 相册内删除时同步删除云端文件:自动清理无用图片,避免图床残留。
- 图片预处理设置:压缩质量
85%,格式转换为webp,在保证清晰度的同时减少体积。 - 高级重命名:建议使用
{Y}{m}{d}_{str-6}规则,避免文件重名冲突。 - 上传后删除本地文件:上传成功后自动删除本地文件,保持本地整洁。
点击 图床 > 本地上传 > 新增图床配置(按实际使用场景选择),完成后点击保存并设为默认图床。
如采用 预处理 模式,则 自定义域名和网站路径需留空,由 Hugo 模板在构建阶段补全。
GitHub 原始域名 raw.githubusercontent.com 在国内访问较慢,如需加速可填入自定义域名,如:
- jsDelivr CDN:如
https://cdn.jsdelivr.net/gh/<username>/<reponame>@<branch>/。 - Cloudflare CDN:将仓库部署到 Pages 并绑定域名(如
https://img.example.com)。
在 PicList 中,Cloudflare R2 通过 AWS S3 协议接入,可直接按 S3 图床方式配置。
其中,<bucket_id> 是 S3 API,位于 R2 > 存储桶 > 设置 > 常规。
配置 Typora
打开 Typora,点击 文件 > 偏好设置 > 图像,勾选以下选项:
- 插入图片时:选择上传图片,并勾选下方三个选项。
- 上传服务:选择 PicList,并设置 PicList 可执行文件路径。
- 图片语法偏好:仅在使用本地预处理时勾选 优先使用相对路径,其余无需勾选。
配置完成后,点击 验证图片上传选项,确认图片可正常上传。
上传后图片路径示例如下:
| |
⚠️ 本地预处理方式的路径修正:
本地预处理下 Typora 图片路径(如 ../../assets/images/...)能正常预览,但 Hugo 渲染时 无法正确解析,因为 assets 目录不参与 Markdown 路径解析,需通过 Hugo Render Hook 修正路径。
创建以下文件:
| |
💡 本地图床未引用图片的清理:
本脚本仅适用于本地图床,用于扫描 assets/images/ 目录下所有 .webp 格式的图片,与 content/ 目录下 Markdown 文件中引用的图片进行比对,找出未被任何文章引用的图片,并在用户确认后删除它们。
创建以下文件:
| |
运行以下命令,执行脚本:
| |
图床迁移
至此,写作环境已配置完成,日常写作时只需在 Typora 中插入图片,提交代码即可自动完成上传。
如需图床迁移,详见《Hugo 图床迁移:从本地到云端》、《Hugo 图床迁移:从云端到本地》。

