记录一次把 29.3 GB 本地书库静态化进 Hexo 博客(2026-10-10)
记录一次把 29.3 GB 本地书库静态化进 Hexo 博客(2026-10-10)
最坑的不是 30GB 的书,而是一个 186 字节的垃圾注入。
今天把 E:\yunpan_hub 的 932 本书静态化进了博客,部署到 GitHub Pages 上线。这活儿本来看起来只是“导个目录”,结果卡在一个插件污染 JSON 的问题上,差点让整个书库页白屏。
一、为什么选静态化:三条隧道内嵌路线全废
最初想的是把本机书库服务通过隧道嵌进博客。结果三条路线全部排除:demo.fan 域名将要过期,用户确认以后不再用;xEdge 第三方组网免费套餐不含内网穿透;cpolar 免费版域名随机变。既然公网隧道都走不通,只剩方案①:把书库静态化进 Hexo,直接发布到 GitHub Pages,零成本、不依赖本机开机。
确定方案后,第一步就是盘货。
二、先盘货:932 本书只导出元数据+封面
书库原始体积 29.3 GB,肯定不能直接传。所以只导出元数据 + 封面。
实测数据:
| 项 | 数值 |
|---|---|
| 书籍总数 | 932(931 本有封面) |
| 格式分布 | PDF 527 / TXT 173 / MOBI 150 / EPUB 64 / AZW3 18 |
| 有简介 | 237 本 |
| 封面 | 931 张,统一 320×440 JPEG,共 28.1 MB |
| 阿里云分享链接 | books 表 aliyun_share_url / aliyun_file_id 全为空 |
导出脚本写在 D:\blog\.workbuddy\scripts\export_books.py,可重复运行;书库更新后重跑一遍再 hexo g -d 就行。另有一个本地视觉验证用的预览生成脚本 make_preview.py,不参与部署。
写入博客的就三个东西:source/books/index.html(自包含单页,封面墙/搜索/格式筛选/排序/详情弹窗/深链 #id/深浅主题自适应,无外部依赖)、source/books/books.json(312 KB)、source/books/covers/*.jpg(931 张)。_config.butterfly.yml 菜单新增:书库: /books/ || fas fa-book-reader。
三、踩坑:hexo-tag-aplayer 把 books.json 毁了
这是当天最大的坑。
hexo-tag-aplayer 会污染非 HTML 产物。 插件 index.js 的 after_post_render 钩子会把 APlayer 的 <link>/<script> **前置写进 data.content**。Hexo 把 .json 视为可渲染文件,于是 public/books/books.json 被注入 186 字节垃圾,前端 fetch().json() 直接报 Unexpected token '<'。
解决方式很简单:在 _config.yml 里加 skip_render: - "books/**",让整个 books 目录走 processAsset 原样复制。注意 source/books/index.html 如果不加 skip_render,会被 Hexo 当页面渲染、套上主题模板,所以必须一起跳过。
另外还确认了一个历史遗留:线上 atom.xml / search.xml 里各有 10 处 aplayer 标记,不是本次引入的,线上本来就有。
四、验证与环境坑:截图要等 15 秒,rm -rf 被拦
验证分两层:产物逐字节比对和浏览器截图。
产物比对很干净:index.html / books.json / 抽样封面全部 OK,源与 public 一致。浏览器截图时踩了两个环境坑:
- 本沙箱内
agent-browser的 Chromium 静默崩溃(Chrome exited early ... DevToolsActivePort),--no-sandbox无效。改用系统msedge.exe --headless=new --screenshot=可行,但文件是异步写出,调用后需等约 15 秒才能读到。 rm -rf <dir>被 safe-delete 拦截失败;清空产物请用hexo clean。
最终用 Edge headless 分别验证了深色(平均亮度 93.5,#0d0d0d 底)和浅色(199,白底),详情弹窗正常。
五、边界:这页只能看书目,不能在线读
本页只是书库目录(封面墙/搜索/详情)。在线阅读/下载仍需要本机 Flask 服务(E:\yunpan_hub\server.py,监听 8800),暂未接入。
而且 server.py 全部 API 无鉴权:DELETE /api/book/<id> 可删书、POST /api/aliyun/token 可改写云盘 refresh_token。任何情况下都不要直接暴露公网。
六、遗留
_config.yml里url: https://blog.demo.fan、subtitle: blog.demo.fan—— 域名过期后必须改,不然影响 canonical / sitemap / OG。- 要“在线阅读”,得重新议隧道方案,前提是先给那套 Flask API 加鉴权。