一、环境准备与依赖安装
在开始构建档案装订服务之前,需要确保服务器或本地开发环境已经安装了Python 3.8或更高版本。本服务将基于Flask框架提供Web API,利用PyPDF2库处理PDF文件的合并与排序,这是实现档案自动装订的核心技术栈。
请在终端中执行以下命令,创建一个独立的项目目录并进入该目录:
```bash
mkdir archive-binding-service
cd archive-binding-service
```
为了避免环境污染,必须创建并激活Python虚拟环境。在Linux或macOS系统下执行:
```bash
python3 -m venv venv
source venv/bin/activate
```
如果是Windows系统,请执行:
```bash
python -m venv venv
venv\Scripts\activate
```
环境激活后,直接运行以下命令安装核心依赖包。请勿使用旧版本的库,以免出现兼容性问题:
```bash
pip install flask==2.3.3 pypdf==3.12.0 werkzeug==2.3.7
```
安装完成后,为了方便后续部署,建议将当前环境的依赖列表导出到requirements.txt文件中:
```bash
pip freeze > requirements.txt
```
二、项目目录结构设计
为了保证代码的可维护性,请严格按照以下结构创建文件和文件夹。这种结构将配置、核心逻辑和入口文件分离,符合微服务开发的最佳实践。
- archive-binding-service/ (项目根目录)
- config.py (配置文件,定义上传路径、允许的文件类型等)
- service/ (核心业务逻辑目录)
- __init__.py (包标识文件,内容为空)
- binder.py (档案装订的核心处理逻辑)
- static/ (静态文件目录,用于存放生成的装订文件)
- uploads/ (临时文件目录,用于存放上传的原始文件)
- app.py (Flask应用主入口)
- requirements.txt (依赖列表)
请使用以下命令快速创建所需的空文件夹和文件:
```bash
mkdir service static uploads
touch service/__init__.py service/binder.py config.py app.py
```
三、配置文件编写
打开config.py文件,我们需要定义服务运行所需的关键参数。这些参数包括上传文件夹的路径、允许的文件扩展名以及最大上传文件大小。这里直接提供完整的配置代码,请复制粘贴:
```python
import os
获取当前文件的绝对路径,确保在不同路径下启动服务都能正确找到文件夹
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
配置上传文件夹路径
UPLOAD_FOLDER = os.path.join(BASE_DIR, 'uploads')
配置生成文件的下载路径
STATIC_FOLDER = os.path.join(BASE_DIR, 'static')
允许上传的文件类型,仅支持PDF
ALLOWED_EXTENSIONS = {'pdf'}
设置最大上传文件大小为100MB,防止服务器资源耗尽
MAX_CONTENT_LENGTH = 100 1024 1024
```
四、核心装订逻辑实现
这是本服务的核心部分。打开service/binder.py,我们将编写一个类ArchiveBinder,它负责接收文件列表,并根据文件名进行排序,然后合并成一个完整的PDF档案。这里实现了按文件名自然排序的逻辑,确保档案页码顺序正确。
```python
import os
import re
from pypdf import PdfReader, PdfWriter
class ArchiveBinder:
def __init__(self, input_dir, output_path):
self.input_dir = input_dir
self.output_path = output_path
@staticmethod
def _natural_key(key_string):
"""
辅助函数:用于生成自然排序的key,确保 file_2.pdf 排在 file_10.pdf 之前
"""
return [int(text) if text.isdigit() else text.lower()
for text in re.split('([0-9]+)', key_string)]
def bind_files(self):
"""
核心方法:扫描目录下的所有PDF文件,排序后合并
"""
pdf_writer = PdfWriter()
获取目录下所有文件
try:
filenames = [f for f in os.listdir(self.input_dir) if f.endswith('.pdf')]
except FileNotFoundError:
raise Exception("上传目录不存在")
if not filenames:
raise Exception("未找到可处理的PDF文件")
使用自然排序算法对文件名进行排序
filenames.sort(key=self._natural_key)
遍历并合并文件
for filename in filenames:
file_path = os.path.join(self.input_dir, filename)
try:
读取PDF文件
pdf_reader = PdfReader(file_path)
将每一页添加到Writer对象中
for page in pdf_reader.pages:
pdf_writer.add_page(page)
except Exception as e:
遇到损坏文件跳过,但在实际生产中应记录日志
print(f"处理文件 {filename} 时出错: {e}")
continue
将合并后的内容写入输出文件
with open(self.output_path, 'wb') as output_file:
pdf_writer.write(output_file)
return self.output_path
```
五、API服务接口开发

接下来编写app.py,创建Flask应用。我们需要实现两个主要功能:一是上传文件接口,支持多文件上传;二是下载装订后文件的接口。为了确保文件名唯一,我们将使用UUID重命名上传的文件。
```python
import os
import uuid
from flask import Flask, request, jsonify, send_from_directory
from werkzeug.utils import secure_filename
from config import
from service.binder import ArchiveBinder
app = Flask(__name__)
app.config.from_object('config')
确保必要的文件夹存在
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(STATIC_FOLDER, exist_ok=True)
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
@app.route('/api/bind', methods=['POST'])
def upload_and_bind():
检查请求中是否包含文件
if 'files' not in request.files:
return jsonify({"error": "没有文件部分"}), 400
files = request.files.getlist('files')
if not files or files[0].filename == '':
return jsonify({"error": "未选择文件"}), 400
为本次请求创建一个唯一的会话目录,防止多用户并发冲突
session_id = str(uuid.uuid4())
session_upload_dir = os.path.join(UPLOAD_FOLDER, session_id)
os.makedirs(session_upload_dir, exist_ok=True)
saved_files = []
for file in files:
if file and allowed_file(file.filename):
使用secure_filename确保文件名安全,并使用UUID防止重名覆盖
filename = secure_filename(file.filename)
save_path = os.path.join(session_upload_dir, f"{uuid.uuid4()}_{filename}")
file.save(save_path)
saved_files.append(save_path)
else:
return jsonify({"error": f"不支持的文件类型: {file.filename}"}), 400
try:
调用核心装订逻辑
output_filename = f"bound_archive_{session_id}.pdf"
output_path = os.path.join(STATIC_FOLDER, output_filename)
binder = ArchiveBinder(session_upload_dir, output_path)
result_path = binder.bind_files()
返回下载链接
download_url = f"/api/download/{output_filename}"
return jsonify({
"message": "档案装订成功",
"download_url": download_url,
"file_count": len(saved_files)
}), 200
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route('/api/download/
', methods=['GET'])
def download_file(filename):
try:
return send_from_directory(STATIC_FOLDER, filename, as_attachment=True)
except FileNotFoundError:
return jsonify({"error": "文件未找到"}), 404
if __name__ == '__main__':
启动服务,监听所有IP,端口5000
app.run(host='0.0.0.0', port=5000, debug=True)
```
六、服务启动与测试
代码编写完成后,即可启动服务。在项目根目录下执行:
```bash
python app.py
```
终端显示Running on http://0.0.0.0:5000即表示启动成功。为了验证功能,我们需要准备两个PDF文件(例如page_1.pdf和page_2.pdf),然后使用curl命令进行测试。
在终端执行以下命令进行API调用(请确保替换为你的实际文件路径):
```bash
curl -X POST -F "files=@/path/to/your/page_1.pdf" -F "files=@/path/to/your/page_2.pdf" http://localhost:5000/api/bind
```
如果一切正常,你将收到JSON格式的响应,包含下载链接:
```json
{
"download_url": "/api/download/bound_archive_xxxx-xxxx-xxxx.pdf",
"file_count": 2,
"message": "档案装订成功"
}
``>
复制返回的download_url,拼接在服务器地址后(例如http://localhost:5000/api/download/bound_archive_xxx.pdf),在浏览器中打开即可下载合并后的完整档案。
七、生产环境部署建议
上述代码使用Flask自带的开发服务器,仅适用于测试和验证。在生产环境中,必须使用Gunicorn或uWSGI作为WSGI服务器,并配合Nginx进行反向代理。
首先安装Gunicorn:
```bash
pip install gunicorn
```
使用以下命令启动服务(-w 4表示启动4个工作进程,可根据CPU核心数调整):
```bash
gunicorn -w 4 -b 0.0.0.0:5000 app:app
```
至此,一个具备文件上传、自动排序、合并打包功能的档案装订微服务已完全落地。该方案零依赖外部复杂组件,仅需Python环境即可运行,非常适合企业内部文档处理流程的自动化集成。