ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

自建文档管理系统 Paperless-ngx:Docker 部署实现 OCR 全文搜索

自建文档管理系统 Paperless-ngx:Docker 部署实现 OCR 全文搜索 1. 从一堆发票和合同说起我为什么会自建一个文档管理系统你有没有算过自己家里和办公室里到底攒了多少张纸合同、发票、体检报告、保修卡、说明书、小孩的作业通知……我以前觉得“放在文件袋里收好就行”结果三年下来文件袋变成了五六个收纳箱真要找某一份文件的时候翻箱倒柜一小时都未必找得到。更要命的是纸质件放久了会受潮、褪色、被虫蛀真正想用的时候上面的字可能已经看不清了。我最早尝试过用扫描全能王之类的 App拍完存到手机里。但这样有几个问题照片质量参差不齐文件多了之后没有统一的命名规则搜索只能靠文件名想在几百张图片里找出一张三年前某次出差的发票基本等于大海捞针。后来我又试过把 PDF 直接扔进坚果云或 OneDrive靠系统自带的文件搜索去找内容识别率低、速度慢、还经常搜不到正文里的关键词。直到我发现了这个叫 Paperless-ngx 的开源项目。它是一个专门用来“吞纸”的文档归档系统简单说就是你把扫描好的 PDF 或图片丢进去它会自动做 OCR 文字识别、自动打标签、自动按日期和文档类型分类然后把所有文件变成可全文搜索的电子档案。我用 Docker 在家里那台旧 NAS 上搭了一套跑了快一年所有纸质资料都找到了归处。这篇博文就把我的搭建过程和踩坑经验完整分享出来给想入坑的朋友一条能直接照着走的路。2. 为什么是 Paperless-ngx它和其他文档方案到底差在哪2.1 它不是一个普通的“网盘”很多人第一次听到自建文档管理第一反应是“我用 Nextcloud 或者 Synology Drive 不就行了吗”确实网盘也能存文件、也能建目录、也能通过文件名搜索。但 Paperless-ngx 的定位完全不同它不关心你存储的“路径”而是关心文档的“内容”和“属性”。你不需要费心去决定这份发票该放在“财务/2024/发票”还是“生活/杂项”里系统会帮你识别出这属于什么类型、什么时候产生的然后自动归档你只需要在需要的时候输入任意关键词把它搜出来。举个例子我把一沓体检报告丢进去系统会自动提取出“体检报告”这个文档类型、识别出“某某医院”“2024年5月”这些元数据并且自动打上“医疗”“报销”之类的标签。想在一年后找到当时某项检查的结果只要在搜索框输入“肝功能”哪怕是图片里的小字也能命中。2.2 和商用系统相比它最打动我的几点我也试用过 Evernote、Notion 这类笔记软件来存档文档还有 Yandex 的 PDF 工具、以及一些收费的 DMS比如 M-Files 之类的企业系统。商用工具要么功能受限要么贵得离谱要么把数据锁在云端。Paperless-ngx 的价值在于它是一个彻底免费的社区开源项目代码都在 GitHub 上没有付费墙数据完全掌握在自己手里。它的核心亮点可以概括成三条自动 OCR 识别内置 Tesseract OCR识别后生成可搜索的全文索引支持包括中文在内的几十种语言。文档消费的自动化设定一个“消费目录”扔进去的文件会被自动处理标记、分类、归档全流程无需人工干预。标签体系很灵活你可以随意定义“重要”“报销”“保修期内”这类自定义标签还能建立多级标签关系比网盘的文件夹强得多。除此以外它还有配套的移动端 App第三方开发的如 Paperless Mobile可以随时随地拍照上传经过内置压缩和优化后转成适合 OCR 的 PDF。这套组合拳打下来基本覆盖了我对个人文档管理的所有需求。3. 搭建之前必须想清楚的三件事存储、硬件和目录规划3.1 硬件到底要多强才能带得动Paperless-ngx 对硬件的要求其实相当亲民。官方文档说最低配置是 1 核 CPU、1GB 内存但我实测过如果文档比较多、搜索量大1GB 内存跑起来会比较吃力尤其是 OCR 识别大量批量扫描件时内存容易吃满。我自己用的是 J4125 这种低功耗四核电迷你主机搭配 8GB 内存跑着 Docker同时在里面装了个轻量 NAS 系统体感非常流畅。如果你只是家里自用一台旧笔记本或者树莓派 4 以上的设备都够。硬盘方面因为要存扫描后的 PDF 和索引数据库建议至少给它分配 100GB 空间。我目前一年加入了大概 3000 多页纸质文档加上数据库和缩略图总共也就占了 15GB 左右所以其实空间压力不大。但要注意PDF 文件尽量存成文本层合并的格式体积小检索也快。3.2 存储结构应该怎么设计很多人以为 Paperless-ngx 会把文件存成你熟悉的“文件/文件夹”结构其实它内部是用 PostgreSQL 数据库来管理元数据而文件本身则以一种“日期/文档ID”的结构存放在磁盘上。我们不需要也不应该去手动改它内部的目录结构否则数据库和文件就对不上了。不过在系统之外你还要规划好“输入”和“备份”这两个目录。输入目录也就是我前面说的消费目录放的是待处理的原始文件备份目录是专门用来存放系统定期导出的数据和加密压缩包。我建议把这两个目录分别放在两块不同的物理硬盘上避免同时损坏。平时往扫描仪或手机里导出的文件统一丢进输入目录由系统自动消费这个过程非常顺手。3.3 命名规则和标签体系的提前规划很多人一上手就狂建标签结果一年后发现标签建了上百个反而比文件夹还乱。我的经验是标签一定要分层规划。我给自己设了一个最笨也最实用的模型按“类型”分类发票、合同、报告、说明书、证件、保单、收据、信函按“状态”分类待处理、待报销、已归档、过期、需要销毁按“来源”分类家庭、车辆、公司、保险、医疗、教育每个文件至少有一个类型标签、一个状态标签、一个来源标签这样组合起来之后搜索时哪怕只记得其中一个维度也能很快找到。这个规划我一开始没做结果后来重新整理了 7000 多份文档累到崩溃。所以这里特别提醒各位一开始就要想好标签树别等文件多了再改。4. 完整部署过程Docker Compose 搭建 Paperless-ngx 的每一行配置4.1 准备一个干净的 Compose 文件官方推荐用 Docker Compose 来跑整套环境因为它需要三个组件协同主程序webserver、PostgreSQL数据库、Redis缓存/消息队列。我自己只在 compose 文件里额外加了一个用于备份的服务用来定时打包数据。可以直接用官方仓库里的docker-compose.yml也可以按照我的精简版来。下面是我个人在用的版本去掉了部分我用不上的环境变量方便理解核心结构version: 3.8 services: broker: image: redis:7-alpine restart: unless-stopped volumes: - redisdata:/data db: image: postgres:15 restart: unless-stopped environment: POSTGRES_DB: paperless POSTGRES_USER: paperless POSTGRES_PASSWORD: your_strong_password volumes: - pgdata:/var/lib/postgresql/data webserver: image: ghcr.io/paperless-ngx/paperless-ngx:latest restart: unless-stopped depends_on: - db - broker ports: - 8000:8000 volumes: - ./data:/usr/src/paperless/data - ./media:/usr/src/paperless/media - ./export:/usr/src/paperless/export - ./consume:/usr/src/paperless/consume environment: PAPERLESS_REDIS: redis://broker:6379 PAPERLESS_DBHOST: db PAPERLESS_DBNAME: paperless PAPERLESS_DBUSER: paperless PAPERLESS_DBPASS: your_strong_password PAPERLESS_OCR_LANGUAGE: chi_simeng PAPERLESS_SECRET_KEY: a_long_random_string PAPERLESS_TIME_ZONE: Asia/Shanghai PAPERLESS_URL: http://your-dns-or-ip:8000 env_file: - docker-compose.env volumes: data: media: pgdata: redisdata:有一点需要注意PAPERLESS_OCR_LANGUAGE字段很多用户用的都是机场云之类的一键脚本默认可能是英文而我这里设的是chi_simeng意味着 OCR 时同时识别简体中文和英文。不设置这一步的话中文文档会被全部识别成乱码或干脆无法检索。4.2 第一次启动的初始化步骤配置文件写好后在目录下运行docker compose up -d第一次启动会自动初始化数据库、迁移表结构、创建管理员账号需要用的初始化操作。之后执行docker compose exec webserver createsuperuser按提示创建你的管理员用户名和密码。然后打开http://your-ip:8000输入账号密码就能看到 Web 界面了。如果只是在本机测试可以直接用http://localhost:8000。但我更建议在正式使用时把它挂在反向代理后面加上 HTTPS 证书这样在外面也能用手机 App 上传文件而不会明文裸奔。后面会专门讲反向代理的配置。4.3 有流程的首次配置从界面到消费目录登录后台后第一件事是设置“文档类型Document Types”和“标签Tags”。在左侧导航栏的“管理”菜单里你可以创建一系列自定义标签然后把这些标签和文档类型关联起来。更重要的是配置“消费目录”的行为。默认情况下Web 界面上传和consume目录里的文件都会被自动处理。建议在管理后台里把“Consumer”相关的选项调成这样允许重复文件覆盖、允许消费后删除原文件、按日期自动创建子目录比如按年月归档。这样处理完的原始文件就不会堆积在输入文件夹里。我这里贴一个简单的环境变量补充可以在docker-compose.env文件里写上PAPERLESS_CONSUMER_DELETE_ORIGINALtrue PAPERLESS_CONSUMER_RECURSIVEtrue PAPERLESS_CONSUMER_SUBDIRS_AS_TAGStrue PAPERLESS_FILENAME_FORMAT{created_year}/{correspondent}/{title}PAPERLESS_FILENAME_FORMAT是给强迫症准备的外部文件名模板可选它会在数据库存好之后以易读的路径形式生成一个副本方便你直接打开文件目录浏览。这个功能很实用我每年年底想按年份拷一份给会计时直接去 data 目录里就能看到按年份归档的文件了。5. 让系统真正“自动”起来工作流、OCR 与全文本搜索的细节5.1 OCR 是系统的灵魂但别直接相信默认参数Paperless-ngx 的 OCR 是基于 Tesseract 的。Tesseract 是一个老牌开源 OCR 引擎识别中文的效果虽然不是顶级但配合 Paperless 的预处理流程去噪、增强对比度、自动旋转日常文档的识别率已经可以接受。如果你拿到的扫描件非常清晰识别率能做到 95% 以上如果是手机随手拍的会差一些但也基本能搜。我有几个提高识别率的小技巧拍照时尽量正对纸面避免倾斜超过 10°系统会自动纠偏但过大角度会明显影响识别。如果文件本身就是数字版 PDF即不是扫描件而是直接从 Word 导出的可以勾选“跳过 OCR”直接提取内嵌文字速度更快、结果更准。对于批量旧扫描件建议先设置PAPERLESS_OCR_MODEredo让它强制重新 OCR 一遍保留原始图片层把文本层加进去。这个模式最耗 CPU但一次性处理完以后搜索体验会好很多。5.2 设置一个万能的“消费目录”这个系统最迷人的地方就是“旋转门式”的处理方式。我在 NAS 上建了一个共享文件夹命名为paperless-consume然后把扫描仪的网络存储路径指到这个文件夹或者用 Paperless Mobile 直接拍照上传。只要这个目录里有新文件出现系统会在几秒内自动把它拿走然后经历OCR 识别 → 提取元数据 → 匹配文档类型 → 打标签 → 存入媒体目录 → 建立全文索引 → 在界面显示。有一个容易忽略的细节consume目录可以递归监听子目录你可以按日期建子文件夹比如每天建一个2025-03-25文件夹方便上班族统一处理当天所有单据。Paperless 会按时间顺序依次消费且默认是一个进程串行处理不会同时启动几百个任务把 CPU 打满。一次扔几百份文件也没事它会排队慢慢处理只是过程中界面会显示“有 X 份文档待处理”的进度条。5.3 全文搜索到底能搜到什么全文搜索支持模糊查询、标签筛选、日期范围筛选以及文档标题、内容、备注、对应人correspondent等字段的组合查询。比如我想查“去年夏天给车买的保险单”我可以直接输入“车险 2024”系统会返回所有符合关键词的匹配项如果关联了文档类型“保单”还可以再加type:保单来过滤。这种搜索能力是传统文件夹式存储完全做不到的。不过也要说实话Paperless 的搜索是基于 PostgreSQL 的全文检索对英文分词比较友好中文分词则是按整个文本块做倒排索引实际体验是——长句子搜不准但关键词搜索完全没问题。比如你搜“增值税”它可以帮你找到所有含“增值税”的发票但你搜“增 值 税”这种带空格的分词可能会漏掉一些记录。所以日常用法是搜小词别一整句话怼进去。6. 我的真实使用链路从纸质件到电子档案的完整操作流6.1 扫描仪怎么选手机 App 怎么用如果你家里有传统馈纸式扫描仪比如 Fujitsu 的 ScanSnap 系列可以直接通过自带的驱动乱序连续扫描多份文件扫描成 PDF 后保存到consum目录即可。我用的是兄弟 ADS-1700W支持自动双面扫扫几百页发票、合同中间无需人工翻面效率非常高。这种设备的价格现在一千五百元左右日常文档量大的话值的。如果你不想买扫描仪直接用手机拍照也能达到可用的效果。Paperless 的官方指令里推荐用“CamScanner”拍完后转成 PDF但我更建议装一个开源的 OpenScan 或 Paperless Mobile第三方的 Flutter 应用。Paperless Mobile 可以直接调取手机相机支持自动裁剪、增强对比度、旋转甚至可以把它设为发送 PDF 的目标应用你在微信里收到一份 PDF 发票选择“用 Paperless Mobile 打开”它会自动通过 API 上传到服务器非常方便。支持 Android 和 iOS 的体验我都测过Android 端略好用一点iOS 端通过“共享”菜单调用也没有问题。6.2 增量录入的老资料怎么处理很多人手上已经有大量纸质历史文件不可能一次性全部扫描进去。我的建议是“按紧急度扫描”先把接下来三个月内肯定要用的资料比如当前的保险合同、保修卡、报销发票处理掉然后把其余文件分类为一箱一箱每周末花半小时处理 30~50 份。这类批量扫描件我都统一命名成“历史档案-日期-编号”这样的格式然后交给系统自动处理。这里有一个小技巧如果你有大量同一个来源的历史文件比如同一家 IT 公司的发票可以在管理后台里配置“对应人Correspondent”把多个邮箱地址或公司名称映射成一个统一名称。这样系统识别为同一家来源后自动归到同一个目录层级下后面按“对应人”搜索会非常痛快。6.3 自动化规则让系统像私人文案助理一样“懂你”Paperless-ngx 在较新的版本中加入了“处理规则Workflow”功能允许你设定条件组合比如“当文档类型为发票并且包含公司名称时自动加上标签增值税、需报销”。用界面操作是这样的进入“设置 → 处理规则”。新建一条规则命名“办公室发票自动归类”。匹配条件选择对应人Correspondent包含“某某贸易”且文档类型包含“发票”。执行动作设置标签“报销”设置对应人为“公司采购”设置标题为“发票-{correspondent}-{date}”。这样配置好之后你会发现日常处理文档基本不需要任何手动操作。扔进consume文件夹的文件直接被精准归置。整个系统变成一个自动化的资料整理流水线。7. 数据是命根子备份、恢复与迁移的正确姿势7.1 文件系统和数据库必须同时备份Paperless-ngx 的所有元数据、标签、对应人、搜索索引全都存在 PostgreSQL 数据库里而 PDF、图片、缩略图等原件存在数据目录中。所以备份必须覆盖两部分漏掉任何一边都不完整。网上有很多人只备份了 Docker 挂载的data和media目录结果数据库丢失恢复后完整性受到很大影响。数据库一旦丢失文件还在但其实就变回一堆没有元数据的散装 PDF 了。我吃过这个亏现在老老实实两条腿走路。我的备份方案是用 Docker volumes 方式跑一个 cron 定时任务每天凌晨打包pg_dump的数据库备份同时把data、media、export目录做成 tar 压缩包然后加密上传到另一台不常开机的 NAS 或对象存储上。具体脚本可以参考下面#!/bin/bash # 每天凌晨3点执行 DATE$(date %Y%m%d) docker exec paperless_db_1 pg_dump -U paperless paperless /backup/paperless_db_$DATE.sql docker exec paperless_webserver_1 tar cf - /usr/src/paperless/data /usr/src/paperless/media | gzip /backup/paperless_files_$DATE.tar.gz # 上传到远程存储 rclone copy /backup/paperless_db_$DATE.sql remote:paperless-backup rclone copy /backup/paperless_files_$DATE.tar.gz remote:paperless-backup用rclone备份到云盘或另一台 NAS可以有效规避“家里被盗/火灾”这种极端情况。备份本身就是文档管理系统的安全网不能省。7.2 灾难恢复流程很简单恢复的时候只需要把 Docker Compose 重新起一套新的然后把数据库备份重放到新的 Postgres 容器里再把文件目录解压回对应路径即可。官方文档里也提供了一条命令document_exporter用来把一个实例的文档批量导出为可读取的格式这在换服务器时特别有用。我之前从一台旧电脑迁到新 NAS 的时候步骤是在新机器上安装 Docker Compose 和一样的配置。停止新启动的 webserver 容器避免它对空数据库做初始化破坏。用pg_restore把旧的 SQL 备份恢复到 Postgres 容器。把备份的文件目录解压覆盖到新media目录。重新启动所有容器。这样操作完登录界面里就能看到所有历史文档标签、分类原封不动搜索索引也依然有效。整个过程大概不到半小时比重新扫描几箱文件要省一万倍时间。8. 我踩过的几个坑希望你们不要再踩一遍8.1 OCR 中文乱码关键是语言包和字体我第一次部署的时候用的是官方默认配置没有设置PAPERLESS_OCR_LANGUAGE结果扫了一份中文合同进去打开后搜索“甲方”什么都搜不到。后来进入容器里检查才发现Tesseract 默认只启用了eng语言包根本没有加载中文识别数据。解决方法是修改环境变量然后重建容器PAPERLESS_OCR_LANGUAGEchi_simeng docker compose up -d --force-recreate webserver注意安装时如果没有手动下载chi_sim.traineddata语言包Tesseract 会报错。在使用官方 Docker 镜像时语言包已经预装好只需修改环境变量即可。如果不放心可以进入容器查看docker exec -it paperless_webserver_1 tesseract --list-langs确认输出里有chi_sim就说明可用。8.2 消费目录不自动处理多半是权限问题有一段时间我把consume目录挂载到 NAS 的 SMB 共享文件夹上结果发现文件丢进去之后迟迟没有反应。查日志才看到Permission denied。Docker 容器内部使用的是 UID 1000 的paperless用户但 NAS 文件夹的 UID 是 1024两边对不上。解决方式是在 compose 文件中加入user: 1000:1000或者直接把宿主机的目录所有权改成 1000chown -R 1000:1000 ./consume之后一切正常。这里也想提醒一下所有挂载目录的数据权限要统一设置否则后续备份、导出都会出现奇奇怪怪的问题。8.3 大量扫描件入库时内存和 CPU 会被吃满当你某天突然一次性把 500 份 PDF 扔进消费目录系统会一个接一个地做 OCR多任务队列会把 CPU 跑满内存如果不够会触发 OOM Killer。如果你用的是小内存机器推荐在环境变量里限制 OCR 并发线程数PAPERLESS_OCR_THREADS2即使 CPU 是四核我也建议只使用 2 个线程做 OCR这样既能保证系统同时干点别的事情也不至于因为内存溢出让整个服务崩溃。吞吐量虽然慢一点但稳定可靠晚上睡觉前丢进去第二天早上基本都能处理完。8.4 升级版本时先备份再升级永远不出错Paperless-ngx 迭代速度很快几乎每个月都有新版本。升级本身很简单就是docker compose pull docker compose up -d。但有时候数据库结构会发生迁移极个别情况下会失败。我经历过一次升级后标签和文档类型的关联表报错最后只能回滚。从那以后我每次升级前都先备份数据库和文件升级后第一时间检查日志确认迁移成功后再把旧备份删掉。升级也是一件要养成习惯的事别让一个运行了一年多的旧版本错过了很多好用的小功能。建议每三个月看一眼 GitHub 的 release选择周末的时间做一次升级。9. 进阶玩法除了管纸质件还能做哪些事9.1 用邮箱自动投递把 PDF 直接发进系统Paperless-ngx 支持配置一个 IMAP 邮箱定时去收取带附件的邮件把附件作为消费源。这意味着你可以把“微信发票”直接发到指定邮箱系统会自己处理然后回传状态。这个功能很适合做报销台账的人公司财务群里发来的电子发票直接转发到邮箱几分钟后 Paperless 里就已经有了。配置在管理后台的“邮箱账户”里填入邮箱服务器和账号密码设置检查间隔为正负 10 分钟注意授权码而不是登录密码。如果你用 QQ 邮箱或 163 这类国内邮箱记得开启 SMTP/IMAP 服务并设置专属授权码。这个功能配合“处理规则”做精细化归档基本做到了全自动。9.2 配合 Home Assistant 实现“扫完即归档”如果你是智能家居玩家可以把扫描仪和 Paperless 联动起来。比如我用一台树莓派的扫描脚本按下扫描仪上的物理按钮后自动调用scanimage扫描并保存为 PDF然后通过curl上传到 Paperless 的 API。整个过程不用开电脑不用动鼠标。家里每个人都能用只需把文件放在扫描仪上按一下按钮后面的事交给系统。这个 API 调用非常优雅curl -s -H Authorization: Token YOUR_API_TOKEN \ -F documentscanned.pdf \ -F title随手扫-$(date %Y%m%d%H%M) \ http://localhost:8000/api/documents/post_document/而 Home Assistant 侧只需要在自动化里调用一个 shell_command把扫描结果放到consume目录即可。我甚至做了一个小面板放在厨房门口需要处理文件时轻轻一按语音播报“扫描成功”系统上的新文档马上就会出现。9.3 用归档保持长期可访问性Paperless-ngx 还有一个“归档文件”功能可以把原始文件压缩成 PDF/A 格式这种格式适合长期存档保证 10 年后用任意 PDF 阅读器打开依然显示正常。对于那些需要长期保存的合同、证书可以在文档详情中选择“归档”系统会生成一个无损的 PDF/A 备份并标记为“已归档”。这个功能在别人的帖子里很少被提到但我个人觉得它是官方最被低估的功能之一。10. 写在最后一个用了 400 天之后的建议如果你要问我“此刻是否值得折腾 Paperless-ngx”我的回答是如果家里的纸质文档积累已经明显让你感到焦虑那一定值得。搭建这套系统并不需要高深的编程能力只要照着上面的 Compose 文件配置、理解基本的环境变量就能在周末完成部署然后开始处理第一批文件。我在实际使用过程中最大的体会是“归档”这个动作的心理成本降低了。以前一想到整理文件总觉得是个大工程要装订、贴标签、分类放进不同文件袋现在完全不用纠结把纸放进扫描仪、按一个按键、过几秒手机上就能搜到。这种无痛的归档习惯直接决定了我能长期坚持下去。最后分享一个我自己养成的习惯每天晚上睡觉前把当天的纸质单据统一放进扫描仪一键扫进consume目录然后第二天早上看一眼 Paperless 处理列表确认没有失败任务。这个习惯我坚持了 400 多天家里的纸质文件数量从一大箱逐渐变成桌上只有“当天待处理”的轻薄一层。与之对应的我在电脑上拥有了一个可以随时全文检索的个人档案库那种踏实感是任何网盘文件夹都替代不了的。
返回列表