一、技术栈选型与环境初始化
针对事业单位档案数字化项目,核心需求是高精度OCR识别、PDF/A长期保存格式支持以及符合DA/T 31标准的元数据管理。本指南采用Python作为核心开发语言,结合Tesseract OCR引擎和MySQL数据库,构建一套零成本、可私有化部署的数字化处理系统。
在Ubuntu 20.04 LTS服务器上执行以下命令进行基础环境依赖的安装。请确保系统已更新至最新内核版本以避免驱动兼容性问题。
1. 系统依赖安装
执行以下命令安装Python环境、图像处理库及OCR引擎:
```bash
sudo apt update
sudo apt install -y python3.8 python3-pip mysql-server mysql-client libmysqlclient-dev
sudo apt install -y tesseract-ocr tesseract-ocr-chi-sim 安装Tesseract及中文语言包
sudo apt install -y ghostscript libimage-exiftool-perl 用于PDF/A转换和元数据提取
pip3 install pymysql pytesseract pdf2image pillow flask
```
2. 数据库初始化配置
登录MySQL并创建专用数据库。事业单位档案数据结构必须严格遵循档号、题名、责任者等核心字段。以下SQL脚本可直接复制执行:
```sql
CREATE DATABASE archive_digital_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE archive_digital_dbi;
CREATE TABLE archives (
id INT AUTO_INCREMENT PRIMARY KEY,
archive_code VARCHAR(50) NOT NULL UNIQUE COMMENT '档号,唯一标识',
title VARCHAR(255) NOT NULL COMMENT '题名',
responsibility VARCHAR(100) COMMENT '责任者',
date VARCHAR(20) COMMENT '日期',
page_count INT DEFAULT 0 COMMENT '页数',
ocr_text LONGTEXT COMMENT '全文识别结果',
file_path VARCHAR(500) NOT NULL COMMENT '数字化文件存储路径',
security_level TINYINT DEFAULT 1 COMMENT '保密等级:1-公开,2-内部,3-机密',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
```
二、核心OCR识别模块开发
OCR是档案数字化的核心。为了提高事业单位常见的红头文件、手写体识别率,我们需要对图像进行预处理(去噪、二值化)。创建名为ocr_engine.py的文件,并写入以下完整代码:
```python
import pytesseract
from PIL import Image, ImageEnhance, ImageFilter
import os
class ArchiveOCR:
def __init__(self, lang='chi_sim'):
self.lang = lang
验证Tesseract是否安装
if not os.system('which tesseract > /dev/null 2>&1'):
pass
else:
raise EnvironmentError("Tesseract未安装,请执行 apt install tesseract-ocr")
def preprocess_image(self, image_path):
"""
图像预处理:转灰度、增强对比度、去噪
"""
img = Image.open(image_path)
转换为灰度图
img = img.convert('L')
增强对比度
enhancer = ImageEnhance.Contrast(img)
img = enhancer.enhance(2.0)
二值化处理
img = img.point(lambda x: 0 if x < 140 else 255, '1')
return img
def recognize(self, image_path):
"""
执行OCR识别
"""
try:
processed_img = self.preprocess_image(image_path)
配置识别参数:psm 6假设为单行文本块,但档案通常是混合文本,使用默认或psm 3
custom_config = r'--oem 3 --psm 3'
text = pytesseract.image_to_string(processed_img, lang=self.lang, config=custom_config)
return text.strip()
except Exception as e:
print(f"识别失败 {image_path}: {str(e)}")
return ""
测试代码
if __name__ == "__main__":
ocr = ArchiveOCR()
请确保当前目录下有test.jpg,否则会报错
print(ocr.recognize("test.jpg"))
```
三、PDF/A格式转换与封装
事业单位档案长期保存必须符合ISO 19005标准(PDF/A)。普通的PDF无法保证长期可读性。我们使用Ghostscript进行转换。创建pdf_converter.py:
```python
import subprocess
import os
def convert_to_pdfa(input_pdf, output_pdf):
"""
使用Ghostscript将普通PDF转换为PDF/A-1b标准
"""
检查输入文件是否存在
if not os.path.exists(input_pdf):
raise FileNotFoundError(f"源文件 {input_pdf} 不存在")
Ghostscript转换命令
-dPDFA: 开启PDF/A模式
-dPDFACompatibilityPolicy=1: 遇到不兼容时报错
cmd = [
"gs",
"-dPDFA",
"-dBATCH",
"-dNOPAUSE",
"-dQUIET",
"-sDEVICE=pdfwrite",
"-dPDFACompatibilityPolicy=1",
"-dColorConversionStrategy=/UseDeviceIndependentColor",
"-dProcessColorModel=/DeviceRGB",
"-sOutputFile=" + output_pdf,
input_pdf
]
try:
result = subprocess.run(cmd, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if result.returncode == 0:
return True
else:
print(f"转换错误: {result.stderr.decode('utf-8')}")
return False
except subprocess.CalledProcessError as e:
print(f"Ghostscript执行失败: {e.stderr.decode('utf-8')}")
return False
```
四、业务逻辑集成与API接口
将OCR、转换和数据库操作封装为Web API。创建app.py,这是整个系统的入口,实现了文件上传、自动识别、归档入库的全流程。
```python
from flask import Flask, request, jsonify
import pymysql
import os
import uuid
from datetime import datetime
from ocr_engine import ArchiveOCR
from pdf_converter import convert_to_pdfa
app = Flask(__name__)
配置项
UPLOAD_FOLDER = '/data/archive_uploads'
STORAGE_FOLDER = '/data/archive_storage'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(STORAGE_FOLDER, exist_ok=True)
数据库连接配置
DB_CONFIG = {
'host': 'localhost',
'user': 'root',
'password': 'your_password', 请修改为实际MySQL密码
'database': 'archive_digital_db',
'charset': 'utf8mb4'
}
def get_db_connection():
return pymysql.connect(DB_CONFIG)
@app.route('/api/upload', methods=['POST'])
def upload_archive():
"""
档案上传接口
参数:file(文件), title(题名), code(档号), responsibility(责任者)
"""
if 'file' not in request.files:
return jsonify({"error": "无文件上传"}), 400
file = request.files['file']
title = request.form.get('title')
code = request.form.get('code')
resp = request.form.get('responsibility')
if not code or not title:
return jsonify({"error": "档号和题名为必填项"}), 400
1. 保存临时文件
temp_filename = f"{uuid.uuid4().hex}_{file.filename}"
temp_path = os.path.join(UPLOAD_FOLDER, temp_filename)
file.save(temp_path)
2. 判断文件类型并处理
ocr_text = ""
final_pdf_path = ""
try:
if file.filename.lower().endswith('.pdf'):
如果是PDF,先转PDF/A
final_pdf_name = f"{code}.pdf"
final_pdf_path = os.path.join(STORAGE_FOLDER, final_pdf_name)
if not convert_to_pdfa(temp_path, final_pdf_path):
return jsonify({"error": "PDF/A转换失败"}), 500
简单的PDF转图片OCR逻辑(需安装pdf2image,此处仅演示第一页)
实际生产环境需遍历所有页面
from pdf2image import convert_from_path
images = convert_from_path(final_pdf_path, first_page=True)
if images:
ocr_engine = ArchiveOCR()
保存第一页为临时jpg供OCR使用
temp_img = os.path.join(UPLOAD_FOLDER, "temp_ocr.jpg")
images[0].save(temp_img, 'JPEG')
ocr_text = ocr_engine.recognize(temp_img)
os.remove(temp_img)
elif file.filename.lower().endswith(('.jpg', '.jpeg', '.png')):
如果是图片,先OCR,再封装为PDF/A
ocr_engine = ArchiveOCR()
ocr_text = ocr_engine.recognize(temp_path)
图片转PDF (使用ImageMagick或PIL,这里简化处理,假设后续手动打包或使用img2pdf)
为简化代码,此处仅记录图片路径,实际需转PDF
final_pdf_path = temp_path
else:
return jsonify({"error": "不支持的文件格式"}), 400
3. 写入数据库
conn = get_db_connection()
try:
with conn.cursor() as cursor:
sql = "INSERT INTO archives (archive_code, title, responsibility, ocr_text, file_path) VALUES (%s, %s, %s, %s, %s)"
cursor.execute(sql, (code, title, resp, ocr_text, final_pdf_path))
conn.commit()
finally:
conn.close()
return jsonify({"message": "归档成功", "archive_code": code, "ocr_preview": ocr_text[:100]})
except Exception as e:
return jsonify({"error": str(e)}), 500
finally:
清理临时文件
if os.path.exists(temp_path) and temp_path != final_pdf_path:
os.remove(temp_path)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
```
五、系统部署与验证
代码编写完成后,需进行服务化部署。使用systemd管理Flask进程,确保开机自启。
1. 修改数据库密码
打开app.py,将your_password替换为MySQL的实际root密码或专用账号密码。

2. 启动服务
为了方便测试,先直接运行:
```bash
python3 app.py
```
服务将在5000端口启动。
3. 功能验证
使用curl命令模拟文件上传,验证OCR和入库流程:
```bash
准备一张测试图片 test.jpg 和 测试PDF test.pdf
curl -X POST -F "file=@test.jpg" -F "title=2023年度工作总结" -F "code=2023-ZONGJIE-001" -F "responsibility=办公室" http://localhost:5000/api/upload
```
预期返回JSON结果包含"归档成功"及OCR识别出的前100个文字预览。此时登录MySQL数据库,执行SELECT FROM archives;即可看到入库的记录。
六、常见问题排查
1. Tesseract识别乱码
请确保已安装tesseract-ocr-chi-sim包。如果识别率低,需在ocr_engine.py中调整二值化阈值(当前为140),或者下载更精确的训练字库替换系统默认字库。
2. PDF转换失败
Ghostscript对某些特殊字体支持不佳。如果报错,尝试在转换命令中添加-dSubsetFonts=true参数,或者检查源PDF是否加密。
3. 权限错误
确保/data/archive_uploads和/data/archive_storage目录对运行Python脚本的用户具有读写权限。通常执行chmod 777 /data/archive_可快速解决测试环境权限问题。