基于FastAPI搭建PDF翻译微服务:从架构到部署(附完整代码)
前言最近在做一个企业内部文档中台项目需要把PDF翻译能力封装成微服务供前端、IM机器人、定时任务等多个消费者调用。调研了一圈发现市面上的方案要么太重直接部署商业软件要么太轻纯脚本无法水平扩展。最终选型是FastAPI PDFTranslator API Docker轻量、异步、易部署。本文分享完整的架构设计和代码实现读者可以直接复制使用。环境准备Python 3.10Docker Docker Compose可选用于部署依赖库pipinstallfastapi uvicorn httpx python-multipart aiofiles架构设计┌─────────────┐ ┌──────────────────┐ ┌─────────────────┐ │ 前端/客户端 │────→│ FastAPI微服务 │────→│ PDFTranslator │ │ /IM机器人 │ │ (翻译任务管理) │ │ 翻译API │ └─────────────┘ └──────────────────┘ └─────────────────┘ │ ↓ ┌──────────────────┐ │ Redis (可选) │ │ 任务队列/缓存 │ └──────────────────┘核心设计原则异步处理PDF翻译是IO密集型操作FastAPI的异步能力可以高效处理并发状态管理翻译任务异步执行客户端通过任务ID轮询进度容错设计网络异常时自动重试失败任务可重新触发实现步骤Step 1: 项目结构pdf-translate-service/ ├── main.py # FastAPI入口 ├── translator.py # 翻译核心逻辑 ├── models.py # Pydantic模型 ├── requirements.txt └── DockerfileStep 2: 数据模型定义# models.pyfrompydanticimportBaseModel,FieldfromtypingimportOptional,LiteralfromdatetimeimportdatetimefromenumimportEnumclassTaskStatus(str,Enum):PENDINGpendingPROCESSINGprocessingCOMPLETEDcompletedFAILEDfailedclassTranslateRequest(BaseModel):source_lang:strField(defaultauto,description源语言代码)target_lang:strField(...,description目标语言代码如 zh, en, de)webhook_url:Optional[str]Field(None,description完成后的回调URL)classTranslateTask(BaseModel):task_id:strstatus:TaskStatus source_lang:strtarget_lang:stroriginal_filename:strcreated_at:datetime completed_at:Optional[datetime]Nonedownload_url:Optional[str]Noneerror_message:Optional[str]NoneStep 3: 翻译核心逻辑# translator.pyimporthttpximportuuidimportasynciofrompathlibimportPathfromtypingimportOptional PDFTRANSLATOR_APIhttps://api.pdftranslator.org/v1/translateclassPDFTranslatorClient:PDFTranslator API 异步客户端def__init__(self,api_key:Optional[str]None):self.api_keyapi_key self.clienthttpx.AsyncClient(timeout300.0)asyncdeftranslate(self,file_path:Path,target_lang:str,source_lang:strauto)-dict: 异步翻译PDF文件 Args: file_path: PDF文件路径 target_lang: 目标语言代码 source_lang: 源语言代码auto表示自动检测 Returns: API响应字典包含翻译结果URL headers{}ifself.api_key:headers[Authorization]fBearer{self.api_key}data{source_lang:source_lang,target_lang:target_lang}withopen(file_path,rb)asf:files{file:(file_path.name,f,application/pdf)}responseawaitself.client.post(PDFTRANSLATOR_API,datadata,filesfiles,headersheaders)response.raise_for_status()returnresponse.json()asyncdefclose(self):awaitself.client.aclose()Step 4: FastAPI 主服务# main.pyimportuuidimportshutilfromdatetimeimportdatetimefrompathlibimportPathfromtypingimportDictfromfastapiimportFastAPI,File,UploadFile,HTTPException,BackgroundTasksfromfastapi.responsesimportFileResponsefrommodelsimportTranslateRequest,TranslateTask,TaskStatusfromtranslatorimportPDFTranslatorClient appFastAPI(titlePDF Translation Microservice)# 内存存储生产环境建议用Redis 持久化数据库tasks_db:Dict[str,TranslateTask]{}UPLOAD_DIRPath(./uploads)RESULT_DIRPath(./results)UPLOAD_DIR.mkdir(exist_okTrue)RESULT_DIR.mkdir(exist_okTrue)app.post(/translate,response_modelTranslateTask)asyncdefcreate_translate_task(background_tasks:BackgroundTasks,request:TranslateRequest,file:UploadFileFile(...)): 创建PDF翻译任务 - 接收PDF文件和翻译参数 - 返回任务ID客户端通过 /tasks/{task_id} 查询进度 ifnotfile.filename.endswith(.pdf):raiseHTTPException(400,detailOnly PDF files are supported)task_idstr(uuid.uuid4())upload_pathUPLOAD_DIR/f{task_id}_{file.filename}withopen(upload_path,wb)asf:contentawaitfile.read()f.write(content)taskTranslateTask(task_idtask_id,statusTaskStatus.PENDING,source_langrequest.source_lang,target_langrequest.target_lang,original_filenamefile.filename,created_atdatetime.now())tasks_db[task_id]task# 后台异步执行翻译background_tasks.add_task(process_translation,task_idtask_id,file_pathupload_path,target_langrequest.target_lang,source_langrequest.source_lang)returntaskasyncdefprocess_translation(task_id:str,file_path:Path,target_lang:str,source_lang:str):后台执行翻译任务tasktasks_db[task_id]clientPDFTranslatorClient()try:task.statusTaskStatus.PROCESSING# 调用翻译API带重试resultawaittranslate_with_retry(client,file_path,target_lang,source_lang)# 保存结果这里模拟下载翻译后的文件result_pathRESULT_DIR/f{task_id}_translated.pdfawaitdownload_result(result[download_url],result_path)task.statusTaskStatus.COMPLETED task.completed_atdatetime.now()task.download_urlf/download/{task_id}exceptExceptionase:task.statusTaskStatus.FAILED task.error_messagestr(e)finally:awaitclient.close()# 清理上传的原始文件iffile_path.exists():file_path.unlink()asyncdeftranslate_with_retry(client:PDFTranslatorClient,file_path:Path,target_lang:str,source_lang:str,max_retries:int3)-dict:带重试机制的翻译调用forattemptinrange(max_retries):try:returnawaitclient.translate(file_path,target_lang,source_lang)excepthttpx.HTTPStatusErrorase:ife.response.status_code500andattemptmax_retries-1:wait_time2**attempt# 指数退避awaitasyncio.sleep(wait_time)continueraiseraiseException(Max retries exceeded)asyncdefdownload_result(url:str,save_path:Path):下载翻译结果asyncwithhttpx.AsyncClient()asclient:responseawaitclient.get(url)response.raise_for_status()withopen(save_path,wb)asf:f.write(response.content)app.get(/tasks/{task_id},response_modelTranslateTask)asyncdefget_task_status(task_id:str):查询任务状态iftask_idnotintasks_db:raiseHTTPException(404,detailTask not found)returntasks_db[task_id]app.get(/download/{task_id})asyncdefdownload_translated_file(task_id:str):下载翻译后的PDFiftask_idnotintasks_db:raiseHTTPException(404,detailTask not found)tasktasks_db[task_id]iftask.status!TaskStatus.COMPLETED:raiseHTTPException(400,detailTask not completed yet)result_pathRESULT_DIR/f{task_id}_translated.pdfifnotresult_path.exists():raiseHTTPException(404,detailResult file not found)returnFileResponse(result_path,filenameftranslated_{task.original_filename},media_typeapplication/pdf)app.get(/health)asyncdefhealth_check():健康检查端点return{status:healthy,timestamp:datetime.now().isoformat()}if__name____main__:importuvicorn uvicorn.run(app,host0.0.0.0,port8000)Step 5: Docker 部署配置# Dockerfile FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . RUN mkdir -p uploads results EXPOSE 8000 CMD [uvicorn, main:app, --host, 0.0.0.0, --port, 8000]# docker-compose.ymlversion:3.8services:pdf-translate-api:build:.ports:-8000:8000volumes:-./uploads:/app/uploads-./results:/app/resultsenvironment:-PYTHONUNBUFFERED1restart:unless-stoppedhealthcheck:test:[CMD,curl,-f,http://localhost:8000/health]interval:30stimeout:10sretries:3运行效果启动服务docker-composeup-d提交翻译任务curl-XPOSThttp://localhost:8000/translate\-Ffiledocument.pdf\-Frequest{target_lang:zh}查询任务状态curlhttp://localhost:8000/tasks/{task_id}响应示例{task_id:550e8400-e29b-41d4-a716-446655440000,status:completed,source_lang:auto,target_lang:zh,original_filename:document.pdf,created_at:2026-07-28T10:30:00,completed_at:2026-07-28T10:32:15,download_url:/download/550e8400-e29b-41d4-a716-446655440000,error_message:null}生产环境建议持久化存储用PostgreSQL替代内存字典存储任务状态消息队列用Celery Redis处理大文件翻译避免阻塞API限流用FastAPI的依赖注入实现速率限制防止滥用监控集成Prometheus Grafana监控API健康度认证添加JWT或API Key认证保护翻译接口总结这套方案的核心价值在于轻量和可扩展单服务启动仅需几十MB内存异步架构天然支持高并发Docker化部署适配K8s等容器编排平台与PDFTranslator的免费API结合企业文档中台的翻译成本可以降到接近零完整代码已在上文给出直接复制即可运行。有问题欢迎在评论区讨论。标签PDF翻译、FastAPI、Python、微服务、Docker、AI翻译

相关新闻

pycharm连接mysql时报错

pycharm连接mysql时报错

#在URL后加上: ?serverTimezoneUTC&characterEncodingutf-8

2026/10/31 14:31:02 阅读更多 →
数据通信基础(一)

数据通信基础(一)

数据通信基础(一) 1.数据通信基本概念 2.数据通信计算 3.通信传输介质 4.数据调制与编码 1.数据通信基本概念 考点1: 信源:信号的产生物(发送端) 信道:通信的通道,是信号传输的媒介 信宿:信号的接收(接收端) 数字信号:是以某一瞬间的状态表示它们传送的消息,…

2026/10/31 14:30:35 阅读更多 →
2026年大模型选型指南:不聊跑分,只讲场景和落地无标题】

2026年大模型选型指南:不聊跑分,只讲场景和落地无标题】

今年的模型格外火,模型跑分各种评论一堆一堆,真要选的时候还是不知道怎么定。 换模型改代码这个事,干过的都懂——改地址、改参数、改返回解析,一折腾就是半天,还得重新测一遍。 这篇不是测评报告,也不是源…

2026/10/30 17:24:45 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/31 0:19:11 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/31 10:20:19 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/31 10:20:23 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/31 0:59:28 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/31 10:30:19 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/31 9:29:43 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/30 17:32:24 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/31 11:00:40 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/31 0:09:01 阅读更多 →