Paperless-ngx 自建文档管理系统实战:扫描件 OCR、中文识别、自动分类规则与全文检索全流程

把纸变成数据:为什么个人站主要自建文档管理系统

我做了这么多年站,最头疼的从来不是技术问题,而是「找东西」。合同、发票、身份证复印件、域名证书、备案材料、供应商报价单——它们散在邮箱附件、微信收藏、几个文件夹,还有一堆扫描件躺在某块移动硬盘里。等到要报税、要对账、要交材料的时候,翻半天翻不出来。更麻烦的是很多是纸质发票和合同,扫描成 PDF 之后连自己都记不住存到哪了。

Paperless-ngx 就是为解决这个问题而生的:它是一个开源文档管理系统,能自动对扫描件做 OCR(光学字符识别)、按内容自动分类打标签、提供全文搜索,然后你就能像用搜索引擎一样检索自己的所有文档。本文记录我用 Docker 部署它、接入扫描仪、配置自动消费规则(consume folder)的完整过程,全程数据留在自己服务器上,不上云。

Paperless-ngx 是什么,和网盘文件夹有什么本质区别

很多人第一反应是「我用网盘 + 文件夹分类不就行了吗」。区别在于三点:

  • OCR 全文检索:网盘只能搜文件名,Paperless 能搜到文档内容里面的字。你扫描了一张纸质发票,搜发票号或者供应商名字,能直接命中,哪怕文件名是 scan_20261001.pdf。
  • 自动分类:你写好规则(比如「包含『增值税』的文档 → 打上『发票』标签,归到『财务』通讯簿」),新文档进来就自动归档,不需要你手动整理。
  • 自托管且不依赖第三方:所有 PDF、缩略图、OCR 文本、元数据都在你自己的服务器和数据库里。对于合同、证件这类敏感文档,这一点比功能本身还重要。

它的前身是 Paperless(原本基于 Python/Django)和 Paperless-ng(社区 fork),Paperless-ngx 是当前活跃维护的正式继承人,文档完善、更新勤快,是目前社区公认的首选。

架构与部署前提:几个必须先想清楚的点

Paperless-ngx 不是一个单容器应用,它由几部分组成:

  • Web 应用(Django):提供界面和 API。
  • 消费目录监听器:监视一个文件夹,丢进去的 PDF/图片会被自动摄入(consume)。
  • OCR 引擎:默认用 Tesseract,中文识别需要额外语言包。
  • 数据库:可以用 SQLite(单人够用)或 PostgreSQL(更好,推荐)。
  • Redis:做任务队列,处理 OCR、分类等异步任务。

硬件上,OCR 是 CPU 密集型任务,一台 2 核 2G 的机器跑起来没问题,但摄入大量扫描件时 CPU 会飙高。磁盘建议留 20GB 以上,因为每份文档会存原文件 + 归档 PDF(带 OCR 文本层)+ 缩略图。

⚠️ 一个很关键的部署前决定:中文 OCR 必须装 tesseract-ocr-chi-sim 语言包,官方镜像默认只有英文。不装的话,扫描的中文文档 OCR 出来是空白或乱码,全文检索形同虚设。这是新手第一个大坑。

用 Docker Compose 部署:镜像、数据与配置

官方推荐用 Docker Compose 部署,我按官方示例改了一版。先建目录结构:

/opt/paperless/
  docker-compose.yml
  docker-compose.env      # 环境变量
  data/                   # 数据库、索引、缩略图
  media/                  # 原始文档与归档 PDF
  consume/                # 丢文件进来就会被摄入
  export/                 # 导出备份用

docker-compose.env 是核心配置文件,几个必改项:

PAPERLESS_URL=https://docs.example.com
PAPERLESS_TIME_ZONE=Asia/Shanghai
PAPERLESS_OCR_LANGUAGE=chi_sim+eng     # 中文 + 英文,关键!
PAPERLESS_ADMIN_USER=admin
PAPERLESS_ADMIN_PASSWORD=改成一个强密码
PAPERLESS_SECRET_KEY=用openssl生成一串随机字符串
PAPERLESS_OCR_OUTPUT_TYPE=pdfa-2       # 归档格式,长期保存友好
PAPERLESS_FILENAME_FORMAT={{ created_year }}/{{ correspondent }}/{{ title }}

PAPERLESS_OCR_LANGUAGE=chi_sim+eng 就是要中文正确识别的关键。注意 Paperless 用的语言名是 Tesseract 的命名(chi_sim 是简体),不是 zh-CN。想识别繁中再加 chi_tra。

然后 docker-compose.yml 大致如下(简化版,实际官方给的是完整五服务编排):

services:
  webserver:
    image: ghcr.io/paperless-ngx/paperless-ngx:latest
    restart: unless-stopped
    depends_on:
      - db
      - broker
    ports:
      - "127.0.0.1:8000:8000"     # 只绑回环,外部走反代
    volumes:
      - ./data:/usr/src/paperless/data
      - ./media:/usr/src/paperless/media
      - ./export:/usr/src/paperless/export
      - ./consume:/usr/src/paperless/consume
    env_file: docker-compose.env

  db:
    image: postgres:16
    restart: unless-stopped
    volumes:
      - ./pgdata:/var/lib/postgresql/data
    environment:
      POSTGRES_DB: paperless
      POSTGRES_USER: paperless
      POSTGRES_PASSWORD: 换一个强密码

  broker:
    image: redis:7
    restart: unless-stopped
    volumes:
      - ./redisdata:/data

启动:docker compose up -d。首次启动 webserver 会花几分钟做数据库迁移和初始化,日志里会刷一大片 migration 输出,这是正常的,别以为卡死了。看到 Starting development server 或 gunicorn 监听日志就说明好了。

服务起来后本地 curl http://127.0.0.1:8000 能拿到登录页面,用 PAPERLESS_ADMIN_USER 和密码登录。

接入扫描仪:三种把纸质文档送进系统的路径

Paperless 本身不驱动扫描仪,它只负责「摄入」。把纸质文档变成数字的有三条路:

  1. 消费目录(Consume Folder)——最通用。你把扫描好的 PDF 或图片丢进 /opt/paperless/consume/,Paperless 会在几秒内自动摄入。可以用 scp、可以是你扫描仪 App 的 SMB/WebDAV 目标(前文写过 Nginx 搭 WebDAV),也可以直接放在服务器共享目录里。
  2. 邮件摄入。配置一个邮箱,Paperless 定时收信并摄入附件。适合「别人发来的合同/发票直接进系统」的场景,需要配 IMAP 账号。
  3. 扫描仪直接推。支持「扫描到 SMB/FTP/Email」的打印机,把目标指向服务器的 SMB 共享,扫描完直接落进消费目录。

我个人的组合是:家用扫描仪通过 SMB 推到服务器共享目录,那个目录被我软链接到 consume 目录;手机拍照的发票则走邮件摄入。两种来源统一汇聚,进系统后统一 OCR。

这里有个细节:消费目录里的文件被摄入后会移动到 media 目录,原文件不会留在 consume。所以如果你想保留一份「原始未经处理」的副本,不要让扫描仪直接写 consume,而是先落到一个暂存目录,再由脚本复制到 consume。这样有「原始件」和「已处理件」两份,多一层保险。

配置 OCR、自动分类规则与标签体系

Paperless 真正的威力在自动化规则上。进入「设置」,重点配这几块:

1. 消费目录规则(Consume Templates / Storage Paths):定义摄入的文档存到哪个物理子目录、按什么格式命名文件。文件名格式用占位符,比如 {{ created_year }}/{{ correspondent }}/{{ title }},让系统自动按年份和往来方分层。这比手动建文件夹强太多。

2. 自动匹配规则(Matching Rules):给每个「往来方(Correspondent)」「文档类型」「标签」设置匹配条件。Paperless 支持三种匹配方式:

  • 内容匹配:文档 OCR 后的文本包含某个关键词(比如包含「供电局」就归到「电费」类型)。
  • 文件名匹配:适合扫描仪输出的命名有规律的情况。
  • 自动分配标签:可以设置「匹配此往来方的文档自动打上 X 标签」。

设置得当的话,新文档进来几乎不需要人工干预:OCR 出「中国移动」,自动归到「电信」往来方、打上「月账单」标签、存进对应目录。你只需要偶尔校对算法猜错的那几份。

3. 标签体系设计建议:不要一开始就建太多标签,容易变成手动分类的负担。我建议三层结构:文档类型(发票/合同/证件/说明)、往来方(谁发的)、状态(待处理/已归档/重要)。80% 的检索靠「往来方 + 日期」,标签只作为补充维度。

中文 OCR 的准确率需要耐心调:Tesseract 对印刷体尚可,但对手写、传真件、倾斜扫描的识别率明显下降。扫描时尽量保持平整、用 300dpi、黑白模式,识别率会高很多。

反向代理、HTTPS 与移动端使用

和前面所有自托管服务一样,公网访问走 Nginx 反代 + Let's Encrypt,绝不直接暴露 8000 端口:

server {
    listen 443 ssl http2;
    server_name docs.example.com;

    ssl_certificate     /etc/letsencrypt/live/docs.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/docs.example.com/privkey.pem;

    client_max_body_size 200M;    # 大 PDF 上传

    location / {
        proxy_pass         http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_read_timeout 300s;   # OCR 大文件耗时长,别设太短
        proxy_buffering    off;
    }
}

client_max_body_size 要放够,扫码生成的多页 PDF 可能几十 MB,默认 1M 会直接 413。proxy_read_timeout 也别太短,虽然 OCR 是异步任务,但上传后的一些同步操作可能耗时。

Paperless 没有官方移动 App(历史上有个社区 App 但维护状态一般),移动端主要靠响应式 Web 界面——它做的 PWA 在手机上体验还可以,建议在浏览器里「加到主屏幕」,配合手机的系统分享(把文件分享到「添加到主屏幕」的 Paperless)就能把手机里收到的 PDF 直接上传。如果你需要更强的移动端,可以调它的 REST API 自己接(Paperless 的 API 文档很完整,支持 token 认证)。

备份、升级与长期维护

Paperless 的数据分三部分,备份策略要分别对待:

  • media 目录:原始文档 + OCR 归档 PDF + 缩略图。这是最核心的资产,必须异地备份。
  • data 目录:搜索索引、配置。可以重建(重新摄入即恢复),但重建耗时,最好也备份。
  • PostgreSQL 数据库:存元数据、标签、往来方关系。必须导出备份,用 pg_dump,别直接拷数据目录文件。

Paperless 官方提供导出功能(Document Exporter):docker compose exec webserver document_exporter ../export,它会把所有文档和元数据导成一个可移植的目录结构,是跨机器迁移或长期归档的首选方式。我把它加进每周 cron,导出后让 restic 同步到异地。

升级很直接:docker compose pull && docker compose up -d。升级前务必 pg_dump 备份数据库和 media 目录——Paperless 做过好几次数据库结构大迁移(尤其从 ng 到 ngx 那次),虽然有自动迁移,但没有备份一旦出岔子就是灾难。

维护上一个常被忽略的点是索引大小。随着文档增多,全文索引和缩略图会占用可观磁盘。定期检查 data/index 目录大小,如果太大可以重建索引(document_index reindex)。另外,OCR 队列如果积压(大量文档同时摄入),可以调高 PAPERLESS_TASK_WORKERS,但注意别把 CPU 占满影响服务器上其它服务。

六个坑总结

  1. 没装中文 OCR 语言包:PAPERLESS_OCR_LANGUAGE=chi_sim+eng 不设,中文文档识别全废。
  2. 端口暴露公网:8000 直接对外,文档系统等于公开。永远只绑回环 + 反代。
  3. client_max_body_size 太小:多页扫描 PDF 上传 413,配够 100M+。
  4. 不做数据库备份:只备份 media 不导出数据库,元数据全丢,标签往来方要重来。
  5. 扫描质量差导致 OCR 错漏:300dpi 平整扫描,别用手机歪着拍。
  6. 升级前不备份:数据库结构迁移不可逆,升级前先 pg_dump 和导出 media。

自建文档管理系统的回报是长期的:你花一个周末把历史文档全扫进去,之后十年里每一次「要交 XX 材料」「上次那份合同在哪」的问题,都能在几秒内用搜索解决。数据在自己手里,检索速度比翻网盘快得多,这才是我坚持自托管的理由。

Last modification:October 15th, 2026 at 07:26 pm

Leave a Comment