paperless-ng 版本演进全解读:从 changelog 到源码的完整升级指南
paperless-ng 版本演进全解读:从 changelog 到源码的完整升级指南
paperless-ng 是一款用于扫描、索引和归档纸质文档的开源文档管理系统,本篇文章以仓库中的 docs/changelog.rst 为核心脉络,逐版本梳理 paperless-ng 从 0.0.1 到 1.5.0 的关键演进,涵盖 OCR 架构重构、全文检索、自动匹配、邮件消费、Docker 部署、ASGI 实时状态等核心模块,并结合 src/paperless/settings.py、docker/docker-prepare.sh、src/paperless/urls.py 等源码给出配置与命令的落地细节。读完本文,你将理解 paperless-ng 各版本的能力边界、破坏性变更的影响范围,以及升级、迁移与运维的完整操作路径。
一、文档定位:一份浓缩的项目技术史
docs/changelog.rst 是 paperless-ng 仓库内唯一一份覆盖全生命周期的变更记录,从 0.0.1「Initial release」一路记录到 1.5.0。它不仅是版本清单,更是一份技术决策史:OCR 引擎从自研 tesseract 封装切换到 OCRmyPDF、前端从 Django Admin 迁移到 Angular 单页应用、文档存储从默认 GPG 加密改为默认明文、Web 服务从 WSGI 升级为 ASGI……每个转折点都伴随着环境变量、数据库迁移与部署方式的连带变化。
因此,本文按「时间线梳理 + 核心功能纵深 + 升级实操」三层组织:先给出从 0.x 到 1.5.0 的完整演进地图,再深入挖掘 changelog 反复提及的关键模块(OCR、搜索、匹配、邮件、Docker、ASGI 通知)在源码中的真实实现,最后把散落在各版本中的破坏性变更与迁移命令汇总成一张可直接执行的升级清单。
二、版本演进时间线:从 0.0.1 到 1.5.0 的能力变迁
2.1 上古时代(0.0.x):功能奠基与早期架构
0.x 早期版本奠定了 paperless 的核心形态:
- 0.0.1:Initial release,项目诞生。
- 0.0.2:加入语言检测(
settings.TESSERACT_LANGUAGE更名为settings.OCR_LANGUAGE)、为document_exporter增加时间戳。 - 0.0.3:加入基础 tagging(标签)。
- 0.0.4:加入基于关键字匹配的自动打标,从 admin 移除
User和Group。 - 0.0.5:支持图片类文档(png、jpg、gif、tiff),加入 HTTP POST 导入、IMAP 邮件支持和 re-tagging 工具。
- 0.0.6:
Pit贡献了并行 OCR 支持与更快的语言检测。
0.1.0 起开始成形:引入 Docker 支持、简单 REST API、临时目录化消费流程、多线程 OCR;0.1.1 中「sender」全面更名为「correspondent」(发件人→通信方),并重写导出器、新增导入器以实现不依赖文件名的元数据保留。
2.2 0.3~0.5:消费流程、认证与界面分水岭
- 0.3.0:接入 django-filter 1.x,加入系统自检(system checks),消费循环时间可通过
PAPERLESS_CONSUMER_LOOP_TIME配置(默认 10 秒);移除了PAPERLESS_CONVERT、PAPERLESS_CONSUME、PAPERLESS_SECRET,分别由PAPERLESS_CONVERT_BINARY、PAPERLESS_CONSUMPTION_DIR、PAPERLESS_SHARED_SECRET取代——这是 changelog 中第一次出现成规模的环境变量重命名。 - 0.3.2:
ALLOWED_HOSTS默认["*"],可用PAPERLESS_ALLOWED_HOSTS覆盖。 - 0.3.5:文档列表从表格改为平铺卡片式界面,支持每页条数配置,允许用户自定义
SECRET_KEY,并把 dotenv 加载移到settings.py顶部。 - 0.3.6:API 支持更新文档的 correspondent 与 tags;
content字段变为可选;消费代码重构为可插拔接口。 - 0.4.0:引入 reminders(提醒)功能,可通过 API 使用。
- 0.5.0:自动打标/自动通信方系统支持模糊匹配;导出/导入脚本支持缩略图;依赖升级到 pyocr 0.4.7 以兼容 Tesseract 4。
2.3 1.x(旧主线):Django 升级与邮件消费成熟
1.0.0 升级 Django 1.11,支持多词匹配自动打标;1.2.0 起 Docker 镜像改为基于 Alpine(体积大幅缩小),并加入「PDF 已有文字则跳过 OCR」的自动判断(可用 PAPERLESS_OCR_ALWAYS=YES 覆盖,且由此依赖 libpoppler-cpp-dev);1.3.0 支持免登录运行(PAPERLESS_DISABLE_LOGIN="true");1.4.0 为 consumer 增加命令行选项,包括 --directory、--loop-time、--mail-delta 与 --one-shot。
2.4 2.x:GPG 加密默认关闭与前端现代化
2.0.0 是旧主线的分水岭:默认不再用 GPG 加密文档。changelog 给出了详细理由——磁盘文件只要有密钥就能读,而密钥通常就存在配置文件里,且文档正文内容从未真正加密。老用户可通过如下命令迁移到明文存储:
./manage.py migrate && ./manage.py change_storage_type gpg unencrypted
然后从 paperless.conf 或环境中移除 PAPERLESS_PASSPHRASE。这一逻辑至今仍保留在 src/paperless/settings.py 的注释中:只有显式设置 PAPERLESS_PASSPHRASE 才启用加密,否则默认明文。
2.2.0 支持 Django 2.0 与 Python 3.7;2.3.0 支持消费纯文本与 markdown 文档、文档内联预览;2.4.0 加入批量打标/批量设置通信方、导入导出按存储类型智能决策;2.5.0 引入 optipng 优化缩略图(可用 PAPERLESS_OPTIPNG_BINARY 指定路径),slug 不再可编辑而是由 slugify() 派生;2.6.0~2.7.0 主要是依赖升级、IMAP 错误处理与 API 字段补充。
2.5 paperless-ng 时代(0.9.x→1.5.0):现代 paperless 的重生
changelog 后半部分属于 paperless-ng 分支(Angular 前端 + 完整 REST API + 任务调度器),这是当前仓库 src/paperless 与 src-ui 对应的代码形态:
- 0.9.0:全新 SPA 前端、Dashboard、全文检索(高亮/自动补全/评分)、可保存过滤器、日志查看器;引入文档类型(Document Types)、收件箱标签(Inbox tags)、基于神经网络的自动匹配、归档序列号(ASN);启用 Django 内置用户管理;REST API 外键改为 ID 引用;
PAPERLESS_DBHOST一设置即切换 PostgreSQL;移除提醒功能;Docker 镜像不再内置 SSL;consumer 改用 watchdog 库与任务调度器并行消费。 - 0.9.4:搜索支持按标签/类型/日期/通信方过滤(需执行一次
document_index reindex);提供拼写纠正;文档可在浏览器内直接预览。 - 0.9.5:OCR 引擎切换为 OCRmyPDF,产出可选中文字的 PDF/A 归档件;
PAPERLESS_OCR_LANGUAGE成为唯一语言来源;document_archiver命令为存量文档补建归档版;消费目录支持递归子目录并以子目录名作为标签(PAPERLESS_CONSUMER_RECURSIVE/PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS);API 支持 token 认证与自定义标题/通信方/标签/类型的上传。 - 0.9.7:saved views 改为服务端存储并与用户账号绑定;新增
PAPERLESS_COOKIE_PREFIX支持多实例共存;文件名格式新增{tag_list}。 - 0.9.9:批量编辑(增删通信方/标签/类型、批量删除)与「More like this」相似文档搜索。
- 1.0.0:导出器重写为可增量更新(支持
rsync增量备份),导出文件名可遵循PAPERLESS_FILENAME_FORMAT,导出期间锁定媒体目录与数据库以保证一致性;设置中加入语言与日期格式选择器。 - 1.1.0:Dashboard 实时显示文档处理状态(消费检测、成功、失败均推送通知),文档列表与 saved views 实时更新——这要求 ASGI 服务器(Docker 用 gunicorn + uvicorn worker,详见下文);提供标签/通信方/类型智能建议;官方支持 Python 3.9;日志从数据库迁出到轮转文件(
PAPERLESS_LOGGING_DIR、PAPERLESS_LOGROTATE_MAX_SIZE、PAPERLESS_LOGROTATE_MAX_BACKUPS,默认写入PAPERLESS_DATA_DIR/log/)。 - 1.2.0:OCRmyPDF 支持去歪斜(deskew)与自动旋转,默认开启;支持批量下载为 zip;轮询消费间隔默认从 1 秒提高到 5 秒以减少读取半成品文件。
- 1.3.0:REST API 开始版本化(见下文 API Versioning);标签颜色选择器;SSO 远程用户认证头可自定义。
- 1.4.x:Docker 启动权限调整仅在实际需要时执行(大幅缩短启动时间);
PAPERLESS_ADMIN_USER/PAPERLESS_ADMIN_PASSWORD可直接在容器启动时创建超级用户;忽略 Mac 特有文件(.DS_STORE、._XXXXX.pdf);支持PAPERLESS_FORCE_SCRIPT_NAME子路径部署。 - 1.5.0:放弃 Python 3.6;Docker 基础镜像从 Debian Buster 换到 Bullseye、内置 Python 3.9;新增卢森堡语(Luxembourgish)locale;新增可配置的消费目录忽略模式
PAPERLESS_CONSUMER_IGNORE_PATTERNS。
三、核心功能纵深:changelog 背后的源码实现
3.1 OCR 管线:OCRmyPDF + tesseract + 归档 PDF/A
0.9.5 是 OCR 架构的转折点:paperless 从自研 PDF 解析切换到 OCRmyPDF:
| 环境变量 | 默认值 | 说明 |
|---|---|---|
PAPERLESS_OCR_LANGUAGE |
eng |
tesseract 识别语言(ISO 639 三字母代码),0.9.5 起不再自动猜测语言,必须显式设置 |
PAPERLESS_OCR_PAGES |
0(不限) |
仅 OCR 前 n 页 |
PAPERLESS_OCR_MODE |
skip |
skip / redo / force 三选一,控制已含文本的 PDF 是否强制重做 OCR |
PAPERLESS_OCR_OUTPUT_TYPE |
pdfa |
OCRmyPDF 输出类型 |
PAPERLESS_OCR_DESKEW |
true |
1.2.0 起默认开启去歪斜 |
PAPERLESS_OCR_ROTATE_PAGES |
true |
自动旋转错误朝向的页面(阈值为 PAPERLESS_OCR_ROTATE_PAGES_THRESHOLD,默认 12.0) |
PAPERLESS_OCR_CLEAN |
clean |
是否用 unpaper 清理;可显式跳过 |
PAPERLESS_OCR_IMAGE_DPI |
未设置 | 图片缺少 DPI 信息时指定;0.9.7 起缺省按 A4 假定 |
归档命令 document_archiver 的实现在 src/documents/management/commands/document_archiver.py:它按 mime type 选取解析器(get_parser_class_for_mime_type),对存量文档逐一生成 PDF/A 归档件;若某文档尚无归档版本才会处理,除非传入 --overwrite;也可以 --document <id> 指定只处理某一篇。TIKA 集成(PAPERLESS_TIKA_ENABLED)则用来处理 .docx/.doc/.odt 等 Office 文档,需要额外起两个服务(解析/元数据 + 转 PDF),默认关闭。
3.2 全文检索与搜索索引
全文检索建立在 Whoosh 索引之上(见 src/documents/index.py),支持字段化查询、自动补全与评分。0.9.4 起可按标签、类型、日期、通信方过滤,0.9.9 起支持「More like this」。索引 schema 变化后(0.9.4、1.4.0 全文检索展示改造)都需要重建索引:
document_index reindex
命令实现见 src/documents/management/commands/document_index.py,支持 reindex 与 optimize 两个子命令,以及 --no-progress-bar 选项。optimize 由任务调度器定期执行,能加快查询并保证自动补全正常。
在 Docker 环境下,docker/docker-prepare.sh 通过 .index_version 文件判断索引是否过期,过期时自动执行 document_index reindex——这解释了 changelog 中「Docker 镜像会自动重建索引,你无需任何操作」的承诺。
3.3 自动匹配:从模糊匹配到神经网络
paperless-ng 的自动分类体系支持多种匹配算法,实现在 src/documents/matching.py:
MATCH_ALL/MATCH_ANY:所有/任一关键字命中;MATCH_LITERAL:字面量精确匹配;MATCH_REGEX:正则匹配(0.9.14 修复了非法正则导致 consumer 崩溃的问题;1.2.1 起保存前即校验正则合法性);MATCH_FUZZY:模糊匹配;MATCH_AUTO:基于神经网络的自动匹配,需要先用document_create_classifier训练分类器(1.4.5 修复了分类模型文件写入不完整的问题)。训练由任务调度器在数据变化时自动触发,Docker 环境无需手动干预。
0.9.5 起,消费目录的子目录名可以直接作为标签赋予文档:设置 PAPERLESS_CONSUMER_RECURSIVE=true 开启递归扫描,PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS=true 把子目录名映射为标签。若要按新匹配规则回刷存量文档,使用 document_retagger(支持 -c/-T/-t 指定匹配对象、-i 仅处理收件箱标签文档、-f 覆盖已分配数据)。
3.4 邮件消费与文档消费细节
邮件消费(paperless_mail)在 0.9.0 被完整重写为「可配置过滤器 + 动作 + 多账户支持」,可在 admin 中配置 mail account 与 mail rule;mail_fetcher 管理命令手动触发抓取。0.9.12 起邮件过滤规则支持内联附件参数与附件文件名通配符。
文档消费侧的关键配置集中在 src/paperless/settings.py:
PAPERLESS_CONSUMER_POLLING/PAPERLESS_CONSUMER_POLLING_DELAY(默认 5 秒)/PAPERLESS_CONSUMER_POLLING_RETRY_COUNT:轮询模式下的间隔与重试,1.2.0 把默认等待从 1 秒提高到 5 秒以减少读取到半成品文件的概率;PAPERLESS_CONSUMER_RECURSIVE:递归消费子目录;PAPERLESS_CONSUMER_IGNORE_PATTERNS:1.5.0 新增,以 JSON 数组指定忽略的 glob 模式(相对消费目录),默认值为[".DS_STORE/*", "._*", ".stfolder/*"],正好对应 1.4.4 起忽略 Mac 系统文件的行为;PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS:子目录名作为标签。
文件归档命名则由 PAPERLESS_FILENAME_FORMAT 控制(见 src/paperless/settings.py),支持 {correspondent}、{document_type}、{title}、{created}、{asn}、{tag_list} 等占位符;修改格式后需要执行 document_renamer 让存量文件按新规则改名(0.9.7 修复了 {created_month}/{created_day} 单数字补零问题,1.1.1 起 {asn} 成为可用占位符并把归档文件名同样存入数据库以避免与原始文件名冲突)。
3.5 ASGI 与实时状态通知
1.1.0 引入的实时消费状态通知(WebSocket)是部署形态的分水岭。实现链路如下:
- 路由注册在 src/paperless/urls.py:
ws/status/由StatusConsumer处理; - src/paperless/consumers.py 中
StatusConsumer继承 Channels 的WebsocketConsumer,订阅status_updates组并把状态事件推送到前端; - Docker 侧,docker/supervisord.conf 使用
gunicorn -c /usr/src/paperless/gunicorn.conf.py paperless.asgi:application启动,src/paperless/workers.py 中的ConfigurableWorker继承自uvicorn.workers.UvicornWorker; - worker 数量由 gunicorn.conf.py 中的
PAPERLESS_WEBSERVER_WORKERS控制(默认 2)。
裸机(bare metal)部署若要用状态通知,需要把 paperless-webserver.service 改为 ASGI 启动方式:
ExecStart=/opt/paperless/.local/bin/gunicorn -c /opt/paperless/gunicorn.conf.py paperless.asgi:application
如仍使用纯 WSGI(如 Apache mod_wsgi),paperless 依然可用,但不会收到状态通知;mod_wsgi 用户如需 WebSocket,须另起 ASGI 服务器(gunicorn+uvicorn、daphne 或独立 uvicorn)并把 WebSocket 连接代理过去,详见 docs/faq.rst。
3.6 REST API 版本化与 URL 演进
1.3.0 起 REST API 采用版本化设计,配置见 src/paperless/settings.py(AcceptHeaderVersioning,默认版本 1)。客户端通过 HTTP Accept 头声明版本:
Accept: application/json; version=1
0.9.x 期间 API 发生过多次破坏性重构:外键字段从嵌套对象改为 ID 引用(correspondent、document_type、tags),/fetch/ 系列 URL 重定向到 /api/documents/{id}/download|thumb|preview,/push 重定向到 /api/documents/post_document/——这些重定向至今仍保留在 src/paperless/urls.py。
1.4.0 新增按 ASN 直达文档的 URL 模式(/asn/123);ASN 同时是文件名格式占位符与搜索过滤字段(archive_serial_number 在搜索索引中映射为 asn,见 src/documents/index.py)。1.3.0 还加入了 SSO 场景下的远程用户认证头定制,配合 PAPERLESS_AUTO_LOGIN_USERNAME 可用于 authelia 等单点登录前端。
3.7 Docker 部署与自动运维
Docker 是 paperless-ng 最推荐的部署形态,容器启动流程分两个脚本:
- docker/docker-entrypoint.sh:映射 UID/GID(
USERMAP_UID/USERMAP_GID)、创建目录、调整数据目录权限; - docker/docker-prepare.sh:按需等待 PostgreSQL(
PAPERLESS_DBHOST存在时)、执行migrate(用flock防止多容器并发迁移)、按.index_version判断是否重建搜索索引、按PAPERLESS_ADMIN_USER是否存在调用manage_superuser。
超级用户创建命令实现在 src/documents/management/commands/manage_superuser.py:读取 PAPERLESS_ADMIN_USER(必填)、PAPERLESS_ADMIN_MAIL(默认 root@localhost)、PAPERLESS_ADMIN_PASSWORD;若用户已存在则仅重置密码,否则创建超级用户,缺密码时给出友好提示。
compose 文件位于 docker/compose,提供 sqlite / postgres 与是否启用 tika 的四种组合;0.9.14 起 compose 固定使用 latest 标签,升级只需:
docker-compose pull
同时 docker/install_management_commands.sh 为几乎所有管理命令(document_archiver、document_exporter、document_importer、mail_fetcher、document_index、document_renamer、document_retagger、document_sanity_checker、manage_superuser 等)生成了容器内快捷方式,这是 1.1.1 之后官方推荐的管理命令执行方式——它们确保以 paperless 用户身份运行,规避权限问题。所有命令都支持 --help。
四、升级与迁移实操清单
4.1 常规升级流程
Docker 路线(详见 docs/administration.rst):
- 停止 paperless;
- 按 备份文档 备份数据;
- 拉取新镜像并
docker-compose up——迁移会自动执行; - 确认无异常后以
-d后台运行。
裸机路线(docs/administration.rst):解包新版本、安装依赖、执行数据库迁移;0.9.14 起发布包已包含编译好的翻译消息与收集好的静态文件,compilemessages 与 collectstatic 不再是必选项。
4.2 版本内联的重要迁移命令汇总
changelog 在多个版本中反复提醒的手动操作,汇总如下:
| 场景 | 命令 | 触发版本 |
|---|---|---|
| 全文检索展示重构后重建索引 | document_index reindex |
1.4.0 |
| 支持按标签/类型/日期搜索后重建索引 | document_index reindex |
0.9.4 |
| 批量删除后重建索引 | document_index reindex |
0.9.10 |
| 修改文件名格式后重命名存量文件 | document_renamer |
0.9.6、0.9.7 |
| 为存量文档创建 PDF/A 归档版 | document_archiver(可选 --overwrite、--document <id>) |
0.9.5 |
| 按新匹配规则回刷存量文档 | document_retagger(-c/-T/-t/-i/-f) |
0.9.0 |
| 旧版 GPG 加密迁移明文 | ./manage.py migrate && ./manage.py change_storage_type gpg unencrypted |
2.0.0 |
| 手动触发完整性检查 | document_sanity_checker |
1.1.2 |
| 手动触发邮件抓取 | mail_fetcher |
1.4.0 前 |
4.3 破坏性变更清单(升级必读)
- 2.0.0:默认关闭 GPG 加密;
PAPERLESS_PASSPHRASE成为显式开关。 - 0.9.0:移除 reminders;移除 Django admin 页面定制;Docker 镜像不再内置 SSL(须由反向代理终结 TLS);旧邮件 consumer 被重写;
PAPERLESS_DBHOST的存在与否决定使用 PostgreSQL 还是 SQLite;PAPERLESS_OCR_THREADS由PAPERLESS_TASK_WORKERS与PAPERLESS_THREADS_PER_WORKER取代(并发调优相关默认值实现在 src/paperless/settings.py);PAPERLESS_FORGIVING_OCR移除(行为成为默认);document_correspondents命令由document_retagger取代。 - 0.9.5:OCR 语言不再自动猜测,
PAPERLESS_OCR_LANGUAGE成为唯一来源;多个 OCR 配置项语义变化(详见 configuration-ocr 章节)。 - 0.9.7:saved views 迁移到服务端并与用户绑定,旧视图需重建。
- 0.3.0:
PAPERLESS_CONVERT/PAPERLESS_CONSUME/PAPERLESS_SECRET分别更名为PAPERLESS_CONVERT_BINARY/PAPERLESS_CONSUMPTION_DIR/PAPERLESS_SHARED_SECRET。
4.4 典型运维场景速查
子路径部署:设置 PAPERLESS_FORCE_SCRIPT_NAME=/paperless(1.4.3 修复),即可将实例托管在 https://host/paperless/ 这类子路径下;该值同时参与 BASE_URL、STATIC_URL 的计算(src/paperless/settings.py)。
多实例共存:用 PAPERLESS_COOKIE_PREFIX 为不同实例指定不同 cookie 名,可在同一浏览器同时登录多个实例。
忽略特定日期:PAPERLESS_IGNORE_DATES 以逗号分隔日期字符串,用 dateparser 解析后加入 IGNORE_DATES 集合,让日期猜测逻辑忽略生日等干扰日期(src/paperless/settings.py)。
增量备份:1.0.0 起 document_exporter 支持就地更新既有导出目录(配合 rsync 增量同步),导出期间锁定媒体目录与数据库保证一致性;文件名可遵循 PAPERLESS_FILENAME_FORMAT(--use-filename-format),--delete 可移除不属于当前导出的残留文件。
加密归档(tika 场景):使用 TIKA 时若执行涉及归档文件的迁移,需确保 tika 服务在迁移期间保持运行——docker-compose 会自动处理这一依赖(1.1.1 迁移说明)。
五、版本路线图与迁移建议
纵观 changelog,可以提炼出三条清晰的演进主线:
- OCR 专业化:自研封装 → OCRmyPDF(PDF/A 归档、deskew、自动旋转)→ TIKA 支持 Office 文档,识别质量与格式覆盖面持续提升;
- 前端现代化:Django Admin → Angular SPA(Dashboard、实时状态、批量编辑、相似搜索),配合 ASGI/WebSocket 实现消费状态实时推送;
- 部署标准化:Docker 成为一等公民(compose 模板、自动迁移、自动索引重建、自动建超级用户),发布流程自动化(GitHub Actions,多架构镜像 amd64/armhf/aarch64)。
对仍在旧版本(2.x 或 0.x 早期)的部署,建议按本文 4.3 的破坏性变更清单逐项核对配置;对已处于 1.x 的部署,重点验证 PAPERLESS_OCR_LANGUAGE 与文件名格式是否满足预期,并确认使用了 ASGI 启动方式以获得实时状态通知。所有迁移命令的完整用法与参数,可在容器内执行 <command> --help 查看,或对照 docs/administration.rst 的管理工具章节。