这可能是截至目前我认为最优雅的博客图片管理方案没有之一。
背景
早期
最早,我的博客采用单台 PHP 服务器搭建动态博客,将图片存储至服务器并定期备份。而访客则是通过 CDN 加速,以便快速载入图片。
缺点:
- 备份不及时,大量冗余数据。
- 图片管理操作不便。
中期
后来,尝试改用七牛 OSS + CDN 全家桶,数据量和流量不大,所以一直在用免费额度。另外,截止 2018 年的某个月,早期注册的老用户是一直拥有 无需备案 的无限制测试域名的(然而后来就收到了强制回收的邮件… 国内企业果然不可信)。
缺点:
- 七牛免费服务真心不靠谱,国外访问巨卡,导致我日常在墙外浏览自己博客都特别慢。
- 管理起来同样不方便,备份也不方便。对于我这个洁癖强迫症来说,有多余 Dangling 的文件存在简直不可饶恕。
- 数据放在国内的第三方服务,说不定哪天有什么违规被查水表,方。
后期
更换为 Hexo 后,一直在用大佬的图床:sm.ms。
直到近期,写博客时截图完毕想要上传,却偶然发现图床页面提示:维护升级中,请稍后访问。
接下来大约一整天左右,一直处于无法上传的状态。用过各种免费图床网盘的我不禁内心一紧:难不成,又是要凉了?于是赶紧把之前上传的所有图片统统排查一遍,发现只有极个别图片出现 404。
缺点:
- 如上文所说,免费图床不存在 SLA,可靠性未知,虽然 sm.ms 大佬已经撑了很久,也相对比较靠谱,但还是不放心。
- 每次截图完毕还需要上传,麻烦。
虽然目前基本未受到影响,但却让我忽然警醒 —— 或许是时候尝试一下不使用外部图床了。
方案
基本思路如下,大致分为两部分:
存储(Git-LFS)
根据以上问题,首先需要解决的就是如何存储。直接保存在 Git Repo 内肯定不合适。Git 无法 Diff 二进制文件,如果在博客更新和重构过程中优化、修改、移动这些图片文件,那对于 Git 仓库来说将是「爆炸性」的,你会看到仓库大小成倍增长,CI/CD 执行 Clone 和 Build 时会严重拖慢速度。虽然有方法可 后期清理,但我并不喜欢这种怪异的折中方案,除非仓库已经被二进制文件「污染」。
针对这个问题,Git-LFS 是个不错的解决方案。它是一个 Git 插件,是由 GitHub 而非 Git 官方推出,尽管官网风格的确很像😂。
该项目的基本原理很简单,在本地 Git 上安装一系列 Hooks,通过钩子「拦截」各类操作,例如 Push / Pull / Checkout。在执行这些操作时,自动从 Git-LFS 服务器下载由其管理的二进制文件。
在真正的 Git 仓库内,实际上只保留该二进制文件的哈希等数据。而实实在在的文件数据存储在 Git-LFS 服务器内,在需要的时候才下载。这样既节省仓库的存储空间,另一方面也能节省你的时间和带宽。
安装使用极其简单,以 macOS 为例:
brew install git-lfs
git lfs install # 安装 Hooks
cd your-repo
告知 LFS 追踪某些文件,可使用类似 .gitignore
文件内的 glob 语法,以 *.png
为例:
git lfs track "*.png"
以上命令会创建 .gitattributes
文件,不要删除它,并确保提交到仓库内。
好了,接下来正常执行其它 Git 操作即可。Push 到 GitHub 后,在 Web 页面打开你的文件,可看到以下字样:
Stored with Git LFS
说明文件已经被 Git-LFS 接管并存储啦。
最后,需要注意一点:由于 Git-LFS 是一个「第三方标准与实现」,故并不是所有 Git 服务都支持 LFS 存储。
新增(Shell 脚本)
我平时使用 macOS 自带的 Screenshot 工具,与多数截图工具类似,它可以将图片存储至剪贴板。那么,如何快速将剪贴板内的图片输出为文件,且按照一定格式组织文件名呢?这需要一点小技巧。
推荐一个名为 jcsalterego/pngpaste 的小工具,顾名思义,它就像 macOS 下的 pbpaste
一样,可以在终端快速读取剪贴板并导出,正如在项目首页上描述的那样:
Paste PNG into files, much like pbpaste does for text.
接下来,我需要一个「小脚本」,进行一点包装工作,我将它保存为 ./image.sh
;.
为 Hexo 博客的根目录,下同。
#!/bin/bash -eu
dir=$(dirname $0) # 该脚本所在目录,用于定位图片存储路径,可替换为其它
tmp_file=$(mktemp) # 生成临时文件路径
pngpaste $tmp_file # 将剪贴板图片保存至临时文件
hash=$(md5 < $tmp_file) # 计算图片的哈希值,用于唯一文件名
img="/images/$hash.png" # 拼接图片 URI
mv $tmp_file "$dir/source$img" # 将临时文件移动为最终图片文件
echo "![]($img)" | tee >(pbcopy) # 生成 Markdown 格式的图片链接,输出至 STDOUT 同时写入剪贴板
搞定。此处不采用拼接时间或随机字符串,而是哈希值作为文件名的原因,是为了保证幂等性。
最终效果为,截图软件截屏,终端内运行 ./image.sh
即可将剪贴板内的图片保存为 ./source/images/$hash.png
,简单到几乎不能再简单。
完美解决 Git 仓库大小问题、上传麻烦问题;最重要的是,图片管理与本地文件操作无异(实际就是在操作本地文件😂),无缝使用各类命令或 GUI。