用Python+Flask搭建PDF翻译微服务:从上传到下载完整实战
·
在文档翻译相关的业务场景中,把PDF翻译能力封装成一个内部微服务是常见需求。这篇文章分享一个基于Python+Flask的轻量级实现方案,支持文件上传、调用外部翻译API、返回翻译后的PDF下载链接。代码可直接运行,适合作为项目原型或内部工具使用。
前言
我们团队经常需要把客户发来的英文/日文/德文PDF文档翻译成中文,再分发出去。人工操作不仅慢,而且容易在复制粘贴过程中破坏原排版。于是我用Flask搭了一个简单的PDF翻译服务,核心逻辑是:
- 接收PDF文件上传;
- 调用第三方PDF翻译API;
- 等待翻译完成;
- 返回可下载的翻译后文件URL。
整个服务不到200行代码,部署方便。
环境准备
- Python 3.10+
- Flask
- Requests
- python-dotenv(用于管理环境变量)
pip install flask requests python-dotenv
项目结构
pdf-translate-service/
├── app.py
├── .env
├── uploads/
└── outputs/
核心代码
app.py
import os
import time
import uuid
import requests
from flask import Flask, request, jsonify, send_file
from dotenv import load_dotenv
load_dotenv()
app = Flask(__name__)
UPLOAD_FOLDER = "uploads"
OUTPUT_FOLDER = "outputs"
TRANSLATE_API_URL = os.getenv("TRANSLATE_API_URL", "https://api.example.com/translate")
API_KEY = os.getenv("API_KEY", "")
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(OUTPUT_FOLDER, exist_ok=True)
@app.route("/upload", methods=["POST"])
def upload_file():
"""接收PDF文件上传并提交翻译任务"""
if "file" not in request.files:
return jsonify({"error": "No file part"}), 400
file = request.files["file"]
if file.filename == "":
return jsonify({"error": "No selected file"}), 400
# 仅允许PDF
if not file.filename.lower().endswith(".pdf"):
return jsonify({"error": "Only PDF files are supported"}), 400
# 保存上传文件
task_id = str(uuid.uuid4())
upload_path = os.path.join(UPLOAD_FOLDER, f"{task_id}.pdf")
file.save(upload_path)
# 获取语言参数
source_lang = request.form.get("source_lang", "auto")
target_lang = request.form.get("target_lang", "zh")
# 提交翻译任务
with open(upload_path, "rb") as f:
resp = requests.post(
TRANSLATE_API_URL,
files={"file": f},
data={
"source_lang": source_lang,
"target_lang": target_lang,
"api_key": API_KEY,
},
timeout=30,
)
if resp.status_code != 200:
return jsonify({"error": "Translation API error", "detail": resp.text}), 502
result = resp.json()
task_info = {
"task_id": task_id,
"status": "pending",
"api_task_id": result.get("task_id"),
}
return jsonify(task_info), 202
@app.route("/status/<task_id>", methods=["GET"])
def check_status(task_id):
"""查询翻译任务状态"""
# 实际项目中应存储任务状态到Redis/数据库
api_task_id = request.args.get("api_task_id")
if not api_task_id:
return jsonify({"error": "api_task_id required"}), 400
resp = requests.get(
f"{TRANSLATE_API_URL}/status",
params={"task_id": api_task_id, "api_key": API_KEY},
timeout=10,
)
return jsonify(resp.json())
@app.route("/download/<task_id>", methods=["GET"])
def download_file(task_id):
"""下载翻译完成的PDF"""
output_path = os.path.join(OUTPUT_FOLDER, f"{task_id}_translated.pdf")
# 模拟轮询等待文件就绪(实际应用应使用任务队列)
for _ in range(30):
if os.path.exists(output_path):
return send_file(output_path, as_attachment=True)
time.sleep(1)
return jsonify({"error": "File not ready"}), 404
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)
.env
TRANSLATE_API_URL=https://api.example.com/translate
API_KEY=your_api_key_here
代码说明
上传接口 /upload:接收multipart/form-data格式的PDF文件,保存到本地后提交给翻译API。返回task_id和api_task_id供后续查询。
状态查询接口 /status/<task_id>:轮询翻译API的任务状态。实际生产环境中,建议把状态写入Redis或数据库,而不是依赖请求参数传递。
下载接口 /download/<task_id>:返回翻译完成的PDF文件。示例中用了简单的本地轮询,生产环境建议用Celery或RQ处理异步任务。
运行效果
启动服务:
python app.py
用curl测试上传:
curl -X POST http://127.0.0.1:5000/upload \
-F "file=@sample.pdf" \
-F "source_lang=en" \
-F "target_lang=zh"
返回示例:
{
"task_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "pending",
"api_task_id": "task_12345"
}
可扩展方向
- 异步任务队列:用Celery+Redis替换本地轮询,提升并发能力;
- 文件存储:大文件上传到OSS/S3,避免本地磁盘瓶颈;
- 进度推送:通过WebSocket或SSE向客户端实时推送翻译进度;
- 多语言支持:把源语言和目标语言扩展为可配置列表;
- 日志与监控:增加结构化日志和Prometheus指标,方便排查问题。
总结
这个Flask微服务虽然简单,但已经覆盖了PDF翻译场景的核心链路:上传、翻译、查询、下载。对于内部工具或原型验证来说足够用了。如果要投入生产,重点是把异步处理和状态持久化做好,避免大文件翻译阻塞主线程。
对于没有自己API资源的开发者,也可以直接使用现成的在线PDF翻译服务,把这篇文章的思路作为前端或内部系统集成的参考。
标签:PDF翻译、Python、Flask、AI翻译、后端开发
更多推荐



所有评论(0)