书多了以后问题才真正开始我自己的电子书大概经历了三个阶段。最早是随手丢在移动硬盘里几十本的时候靠文件名就能找到。后来上了飞牛 NAS想着终于可以集中存放就把各种来源的书全部塞进一个books目录。等到数量过千问题就暴露了同名文件重复比如《深入理解计算机系统》有pdf、epub、mobi三个版本文件名还是各种奇怪的下载名元数据完全缺失作者、出版社、ISBN 全靠猜想看某本书得先 SSH 进去find或者用飞牛自带的文件管理一层层点手机上想接着看进度对不上。这些问题的根源不是存储而是缺少一层书库语义。文件系统只认路径不认这本书是谁写的、属于哪个系列。所以我需要的其实是一个能管理元数据、能按作者/标签/系列检索、还能在浏览器和手机上看书的服务。Calibre-Web 正好是这个定位。它是 Calibre 的一个 Web 前端底层复用 Calibre 的书库结构每本书一个目录元数据存在metadata.db这个 SQLite 里对外提供网页界面、OPDS 订阅和在线阅读。需要说清楚的一点Calibre-Web 本身不负责整理元数据它只是把 Calibre 已经整理好的书库展示出来。真正干整理活的是 Calibre 桌面端。这一点决定了后面整个架构——桌面端负责写NAS 上的 Web 端负责读。先把架构想清楚再动手很多教程一上来就讲docker run但如果不先定好谁来写库、谁来读库后面一定会遇到数据库锁或者元数据被覆盖的问题。我最终的方案是这样角色运行位置职责Calibre 桌面端我的笔记本导入书籍、抓取元数据、编辑封面和标签书库目录飞牛 NAS 共享目录存放所有书籍文件和metadata.dbCalibre-Web飞牛 NAS 上的 Docker 容器只读展示、在线预览、OPDS 推送阅读端浏览器 / 手机阅读 App通过网页或 OPDS 取书【关键结论】Calibre-Web 和 Calibre 桌面端不要同时写入同一个书库。Calibre 的metadata.db是 SQLiteCalibre 桌面端在导入书籍时会长时间持有写锁如果此时 Web 端也在写轻则报 database is locked重则元数据错乱。我采用的方式是桌面端写完、关闭后Web 端才用它。如果你确实需要 Web 端也能上传书籍Calibre-Web 是支持这个功能的但它写入的机制和桌面端不完全一致混用有风险。这一点我没有在生产环境长期验证只是从它的实现方式上判断风险较高所以选择了单写多读。目录结构怎么规划飞牛 NAS 的共享目录一般挂在/vol1这类路径下具体路径以你自己的设备为准不同型号和系统版本会有差异。我在共享目录里建了这样一个结构/vol1/1000/books/ ├── calibre-library/ # Calibre 书库根目录metadata.db 在这里 │ ├── metadata.db │ ├── Author Name/ │ │ └── Book Title (123)/ │ │ ├── cover.jpg │ │ ├── metadata.opf │ │ └── book.epub │ └── ... └── inbox/ # 待导入的散书临时目录calibre-library这个目录不要手动去改里面的结构。Calibre 用的是作者名/书名 (数字ID)这种布局那个数字 ID 是数据库主键手动改名会让数据库和文件对不上。要改元数据就在 Calibre 桌面端改。inbox是我自己的习惯用来放还没整理的书。Calibre 桌面端支持添加书籍时从指定目录导入导入完可以顺手清空。在飞牛上跑 Calibre-Web飞牛的 Docker 管理界面可以直接拉镜像也可以用 Compose。我用的是 Compose因为端口、卷映射这些写在一起更清楚迁移也方便。镜像我选的是社区维护的linuxserver/calibre-web这个镜像更新比较活跃。具体版本号建议以你拉取时的 latest 为准我这里不写死 tag避免过时。# docker-compose.ymlservices:calibre-web:image:linuxserver/calibre-web:latestcontainer_name:calibre-webenvironment:-PUID1000-PGID1000-TZAsia/Shanghai-DOCKER_MODSlinuxserver/mods:universal-calibrevolumes:-/vol1/1000/books/calibre-library:/books-/vol1/1000/docker/calibre-web/config:/configports:-8083:8083restart:unless-stopped几个参数值得单独说PUID/PGID必须和你 NAS 上拥有书库目录的那个用户一致。飞牛上如果你用的是默认用户通常是 1000但这个值一定要自己确认在 NAS 上执行id 你的用户名看 uid 和 gid。填错了容器能起来但打开书库会报权限错误。DOCKER_MODS这行是给容器装上 Calibre 的二进制工具集。Calibre-Web 的某些功能比如格式转换、部分元数据读取依赖 Calibre 的命令行工具不装这个 mod 的话镜像里只有 Web 部分。这一点在 linuxserver 的文档里有说明我实测装和不装格式转换功能确实有差别。/books这个挂载点要指向书库根目录也就是metadata.db所在的那一层不是它的上级。我第一次就挂错了一层结果 Web 界面一直提示找不到书库。启动后访问http://NAS地址:8083初始账号密码在 linuxserver 镜像里是admin/admin123。第一件事就是改密码这个默认凭据是公开的。登录后如果提示要填书库路径填/books。中文元数据抓取是最大的坑书库能不能用关键看元数据质量。Calibre 桌面端内置了豆瓣、亚马逊等多个元数据源但豆瓣源在国内环境下的可用性这几年一直在变经常出现超时或者返回空。我在 Calibre 里做的设置是首选豆瓣备用 Google Books 和 Bing。具体路径在首选项 → 元数据下载可以调整数据源的顺序。实际抓取时如果一本书豆瓣抓不到我会手动切到 Google Books 试试。这里有个取舍要讲清楚豆瓣的数据中文书最准尤其是出版社、出版年份、丛书信息Google Books 英文书更全中文书的封面经常缺失手动填 ISBN 再抓命中率会明显高于用书名搜。我的做法是先在 Calibre 里批量选中书籍用下载元数据批量抓一遍然后人工过一遍封面和作者把明显错的改掉。这一步没有捷径指望全自动抓取得到干净的书库目前不现实。抓完元数据后用保存到磁盘或者直接在书库里用转换书籍统一格式。我个人的偏好是主存epub因为它在手机阅读 App 和 Calibre-Web 的在线阅读里兼容性最好。mobi主要是给老款 Kindle 用的pdf保留原样不动。批量导入的脚本化处理几百本书手动一本本导入太慢。Calibre 桌面端有命令行工具calibredb可以脚本化导入。这里给一个我自己用过的 Python 脚本作用是遍历inbox目录把书加到指定书库# add_books.py# 依赖需要本机安装 Calibrecalibredb 在 PATH 里# 我用的是 Calibre 7.xcalibredb 参数以此为准importsubprocessfrompathlibimportPath LIBRARY/path/to/calibre-libraryINBOXPath(/path/to/inbox)defadd_book(file_path:Path)-bool:调用 calibredb 把单本书加入书库try:resultsubprocess.run([calibredb,add,--library-path,LIBRARY,str(file_path)],capture_outputTrue,textTrue,timeout120,)ifresult.returncode0:print(f[OK]{file_path.name})returnTrueelse:print(f[FAIL]{file_path.name}:{result.stderr.strip()})returnFalseexceptsubprocess.TimeoutExpired:print(f[TIMEOUT]{file_path.name})returnFalsedefmain():exts{.epub,.mobi,.azw3,.pdf}files[fforfinINBOX.rglob(*)iff.suffix.lower()inexts]print(f共发现{len(files)}本书)oksum(add_book(f)forfinfiles)print(f导入完成成功{ok}/{len(files)})if__name____main__:main()这里有几个细节是踩过才知道的--library-path必须指向书库根目录。如果省略calibredb会用环境变量或者默认书库容易加错地方。超时设 120 秒。有些体积大的 PDFcalibredb add会去解析内容耗时可能超过一分钟。不加超时的话脚本可能卡住。capture_outputTrue把输出收起来再打印避免几百本书刷屏。但要注意如果某本书导入失败calibredb的报错信息在stderr里不看的话你会以为它成功了。【踩坑提醒】导入前先复制一份metadata.db做备份。calibredb是直接操作数据库的脚本跑一半中断有概率留下半成品记录。我一般会在导入前cp metadata.db metadata.db.bak。Web 端的几个实用配置书库整理好之后Calibre-Web 的默认配置还有几个地方值得调。开启匿名浏览如果只是家庭内网使用可以在管理 → 基本配置里勾选允许匿名浏览省得每次都要登录。这个看你自己的安全需求公网暴露的话千万别开。配置 OPDSCalibre-Web 自带 OPDS 接口地址是http://NAS地址:8083/opds。手机上的静读天下、KyBook、Marvin 这类阅读 App 都支持添加 OPDS 源。配好之后在 App 里就能直接浏览书库、下载书籍比网页端体验好。阅读进度同步Calibre-Web 自己有一个阅读状态记录但它是基于网页阅读器的。如果你用外部 App 看进度不会同步回 Calibre-Web。这一点要看清楚需求——它不是一个跨 App 的进度同步方案只是 Web 端自己的记录。格式转换前面装了 Calibre mod可以在 Web 端对单本书做格式转换。但我还是建议在桌面端批量做Web 端转换慢而且转完的文件管理不如桌面端方便。权限和性能上的实际表现权限这块我前面提过PUID/PGID填错是新手最常遇到的问题。判据很简单容器起来后如果打开书库报 “DB location is not valid, please enter correct path”先别急着改配置去 NAS 上看目录的实际 uid/gid。性能上我的书库目前是几千本规模Calibre-Web 的列表页加载是秒开的。真正慢的是首次扫描书库因为它要读每本书的metadata.opf。几千本的话第一次打开可能会转几十秒之后就正常了。这一点和书库大小强相关如果你有上万本建议先分库管理。关于并发我在前面强调了单写多读。如果你确实想 Web 端和桌面端同时用一个折中的做法是——桌面端只在你需要整理时打开整理完关闭Web 端平时一直开着。这样写入冲突的概率会低很多。但这不代表没有风险只是降低了。这套方案适合谁回头看这套方案的核心不是 Calibre-Web 这个软件而是桌面端整理 NAS 存储 Web 端展示这个分工。想清楚分工配置自然就顺了。它比较适合书多、想集中管理、有 NAS、能接受手动整理元数据的人。它不太适合只想找个地方丢文件、不想花时间整理元数据、期望全自动的人。Calibre 的元数据整理是有学习成本的尤其是中文书前期投入不小。如果你现在正打算做这件事我的建议是先别急着搭 Web 端。拿 50 本书在 Calibre 桌面端把元数据抓一遍感受一下这个流程你能不能接受。能接受再上 NAS 部署。不能接受可能你需要的是别的方案而不是 Calibre-Web。至于我这边下一步想试试的是把书库的元数据导出来做一份索引配合本地检索工具实现按内容找书而不只是按书名找。这个和 Calibre-Web 本身关系不大算是另一个话题了。