一、目标
前面两篇文章,实现了从合同模板到新的合同文字内容的生成,这篇文章提供后端接口实现以及docker一键部署,实现dify http节点传入合同文本,并返回word合同文本下载链接。
二、架构
- nginx
nginx实现生成的word文本对外提供下载访问链接
- python
fastApi对外发布文字转word文档接口
- docker
部署nginx以及python服务。
- docker-compose.yml
通过编排,实现通过命令行一键自动下载镜像以及部署服务。
bash
docker-compose up -d
三、具体实现
创建好如下目录:

全部目录以及文件如下:

nginx配置文件:
javascript
server{ #以下配置只在http部分的server部分中生效
listen 80;
server_name localhost;
location /app/wordSave{ #以下配置只在http/server部分的location部分中生效
root /usr/share/nginx/html;
index index.html index.htm;
}
}
python文件:
mdToword_tool_http_pypandoc.py:
python
#!/usr/bin/env python3
"""
FastAPI服务器工具:将Markdown格式数据生成Word文档(使用pypandoc版本)
"""
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
import os
import base64
import tempfile
import pypandoc
from typing import Dict, Any
from datetime import datetime
from pydantic import BaseModel
# 创建FastAPI应用实例
# 修改应用创建部分,移除请求体大小限制
app = FastAPI(
title="Markdown转Word服务",
version="2.1.0",
description="使用pypandoc的Markdown转Word文档服务",
# 移除请求体大小限制
body_size_limit=None
)
# 定义请求体模型
class ConvertRequest(BaseModel):
markdown_content: str # base64加密后的Markdown内容
output_filename: str = None
# 定义响应模型
class ConvertResponse(BaseModel):
success: bool
message: str
output_path: str
local_path: str
output_filename: str
content_length: int
error: str = None
# 定义信息响应模型
class InfoResponse(BaseModel):
service_name: str
version: str
description: str
supported_formats: list
output_format: str
features: list
def process_markdown_to_word(markdown_content: str, output_filename: str = None) -> Dict[str, Any]:
"""
使用pypandoc将Markdown内容转换为Word文档
Args:
markdown_content: base64加密后的Markdown格式的文本内容
output_filename: 输出文件名(可选)
Returns:
包含转换结果的字典
"""
try:
# 生成当前时间戳(格式:年-月-日_时-分-秒)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
# 如果没有指定输出文件名,生成默认文件名
if not output_filename:
output_filename = f"output_document_{timestamp}.docx"
else:
# 处理用户提供的文件名,添加时间戳
name, ext = os.path.splitext(output_filename)
if not ext:
ext = '.docx'
output_filename = f"{name}_{timestamp}{ext}"
# 获取当前文件所在目录
current_dir = os.path.dirname(os.path.abspath(__file__))
# 设置wordSave文件夹路径
word_save_dir = os.path.join(current_dir, 'wordSave')
# 确保wordSave目录存在
if not os.path.exists(word_save_dir):
os.makedirs(word_save_dir)
# 构建完整的输出路径
output_path = os.path.join(word_save_dir, output_filename)
# 构建nginx可用的相对路径
nginx_path = f"app/wordSave/{output_filename}"
# 解码base64内容
try:
decoded_content = base64.b64decode(markdown_content).decode('utf-8')
except Exception as e:
raise ValueError(f"Base64解码失败: {str(e)}")
try:
# 使用pypandoc转换Markdown到Word
pypandoc.convert_text(
decoded_content,
'docx',
format='markdown',
outputfile=output_path,
# 添加额外的转换选项以获得更好的结果
extra_args=[
'--standalone',
'--wrap=preserve',
# 修改为更适合办公文档的字体设置,确保WPS完全兼容
'-V', 'CJKmainfont=SimSun', # 主字体:宋体(WPS最兼容的基础字体)
'-V', 'CJKsansfont=SimHei', # 无衬线字体:黑体
'-V', 'CJKseriffont=KaiTi', # 衬线字体:楷体
'-V', 'CJKmonofont=FangSong', # 等宽字体:仿宋
'-V', 'mainfont=Times New Roman', # 西文字体:Times New Roman
# 字体大小设置
'-V', 'fontsize=12pt', # 正文字体大小
# 目录设置
'--toc', # 添加目录生成
'--toc-depth=6', # 确保捕获所有6级标题
# 标题页设置
'-V', 'title-page=true', # 启用标题页
'-V', 'title=文档标题', # 文档标题
'-V', 'subtitle=副标题', # 副标题
'-V', 'author=作者名称', # 作者
'-V', 'date=\\today', # 当前日期
# 页面设置
'-V', 'header-includes=\\pagenumbering{gobble}',
# Markdown格式设置
'--markdown-headings=atx', # 明确指定使用atx格式的标题(#开头)
# 保留更多的格式信息
'-f', 'markdown+smart+auto_identifiers+header_attributes+raw_html'
]
)
# 检查文件是否成功创建
if not os.path.exists(output_path):
raise Exception("转换失败:未生成Word文件")
return {
"success": True,
"message": "Markdown转Word成功(使用pypandoc)",
"output_path": "http://127.0.0.1:9999/" + nginx_path, # 返回nginx可用的路径
"local_path": output_path, # 保留本地完整路径用于调试
"output_filename": output_filename,
"content_length": len(markdown_content)
}
except Exception as e:
return {
"message": "转换过程中出现错误",
"error": str(e),
}
except Exception as e:
return {
"success": False,
"error": str(e),
"message": "转换过程中出现错误",
"output_path": "",
"local_path": "",
"output_filename": "",
"content_length": 0
}
@app.post("/convert", response_model=ConvertResponse)
async def convert_markdown_to_word_endpoint(request: ConvertRequest):
"""
将Markdown格式数据转换为Word文档的POST接口
Args:
request: 包含markdown_content(必需)和output_filename(可选)的请求体
Returns:
转换结果,包含成功状态、文件路径等信息
"""
print("接收到转换请求")
if not request.markdown_content:
raise HTTPException(status_code=400, detail="缺少markdown_content参数")
result = process_markdown_to_word(request.markdown_content, request.output_filename)
print(f"转换结果: {result}")
if not result["success"]:
raise HTTPException(status_code=500, detail=result.get("error", "转换失败"))
return result
@app.get("/info", response_model=InfoResponse)
async def get_conversion_info_endpoint():
"""
获取转换服务信息的GET接口
Returns:
服务信息,包括版本、功能等
"""
return {
"service_name": "Markdown转Word服务",
"version": "2.1.0",
"description": "使用pypandoc和FastAPI框架的Markdown转Word文档服务",
"supported_formats": ["markdown", "md"],
"output_format": "docx",
"features": [
"使用pypandoc实现高质量转换",
"支持复杂Markdown语法",
"保留原始格式和样式",
"支持表格、代码块、数学公式",
"文件名自动添加时间戳",
"支持base64编码内容输入",
"可选的目录生成功能"
]
}
@app.get("/")
async def root():
"""
根路径,返回服务基本信息
"""
return {
"message": "欢迎使用Markdown转Word服务(pypandoc版)",
"version": "2.1.0",
"endpoints": {
"convert": "POST /convert - 将Markdown转换为Word",
"info": "GET /info - 获取服务信息"
}
}
if __name__ == "__main__":
import uvicorn
# 运行FastAPI服务器
uvicorn.run(
"mdToword_tool_http_pypandoc:app",
host="127.0.0.1",
port=7777,
reload=True # 开发环境开启热重载
)
Dockerfile:以官方python镜像为基础,创建包含python接口镜像的
bash
# 使用官方Python镜像作为基础镜像
FROM python:3.13.5
# 维护者信息
MAINTAINER 944658413@qq.com
# 设置工作目录
WORKDIR /app
# 复制requirements.txt到工作目录
COPY requirements.txt .
# 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt
# 复制Python代码到工作目录
#COPY mdToword_tool_http.py .
COPY mdToword_tool_http_pypandoc.py .
# 创建wordSave目录用于保存生成的Word文件
RUN mkdir -p /app/wordSave
# 安装pandoc(pypandoc的系统依赖)
RUN apt-get update && \
apt-get install -y pandoc && \
rm -rf /var/lib/apt/lists/*
# 设置卷挂载,将容器中的wordSave目录挂载到外部
VOLUME ["./nginx/wordSave"]
# 暴露Python应用端口(与代码中设置的7777端口一致)
EXPOSE 7777
# 设置环境变量
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
# 启动Python应用
CMD ["uvicorn", "mdToword_tool_http_pypandoc:app", "--host", "0.0.0.0", "--port", "7777"]
requirements.txt:依赖
python
# Markdown转Word服务依赖列表
# Web框架
fastapi>=0.95.0
uvicorn[standard]>=0.22.0
# 文档处理
python-docx>=0.8.11
markdown>=3.4.0
pypandoc>=1.11
# HTML解析
beautifulsoup4>=4.11.0
# 数据验证
pydantic>=1.10.0
# 可选依赖:提高性能
httpx>=0.23.0 # 异步HTTP客户端
python-multipart>=0.0.6 # 表单数据支持
docker-compose:
PS:
两个虚拟机同时挂载同一个wordSave文件夹,可实现生成的word文件在nginx对外发布。
bash
version: '3.8'
services:
wb-dify-nginx:
image: nginx:latest
environment:
- TZ=Asia/Shanghai
container_name: wb-dify-nginx
ports:
- "9999:80"
volumes:
- ./nginx/wordSave:/usr/share/nginx/html/app/wordSave
- ./nginx/logs:/var/log/nginx
- ./nginx/conf.d:/etc/nginx/conf.d
restart: unless-stopped
wb-dify-python:
build:
context: ./python
dockerfile: Dockerfile
environment:
- TZ=Asia/Shanghai
image: wb-dify-python
container_name: wb-dify-python
ports:
- "7777:7777"
volumes:
- ./nginx/wordSave:/app/wordSave
restart: unless-stopped
# extra_hosts:
# - "somehost:162.242.195.82"
# 以上会在此服务的内部容器中 /etc/hosts 创建一个具有 ip 地址和主机名的映射关系:162.242.195.82 somehost
四、结果演示
dify节点,http得到返回以后,后面跟一个内容优化节点达到显示美观的效果

实际演示:

五、其他
docker安装与部署,参考我的这篇文章,很详细了:
dify的部署:
暂时没写,基本上都是梯子去github 克隆以后,去docker文件夹,运行类似的docker-compose.yml一键部署。