
把 Microsoft MarkItDown 做成本地文档转换 APIPDF/Office 转 Markdown 后用 cpolar 接收远程上传同事丢过来一份 PDF、Word 或 PPT想快速变成 Markdown如果每次都手动打开工具、另存、复制流程很碎。更麻烦的是远程同事或自动化流程不在你的电脑旁边没法直接调用你本地的转换环境。这篇文章做一个实用方案把 Microsoft 开源的MarkItDown封装成本地 FastAPI 服务提供一个/convert文件上传接口本机完成 PDF/Office 等文件到 Markdown 的转换需要临时给远程同事或工作流使用时再用 cpolar 暴露本地8000端口。适合这些场景远程同事临时上传 PDF/Word拿回 Markdown 文本自动化脚本把合同、产品文档、会议纪要转成 Markdown本地保留转换环境不把服务长期部署到公网给后续知识库整理、文档清洗、内容归档提供统一入口。说明本文只做“文档转 Markdown API”不延伸到知识库入库也不做模型服务部署。1. 整体流程流程很简单远程用户 / 自动化脚本 │ │ 上传 PDF / DOCX / PPTX / XLSX ▼ cpolar 公网临时地址 │ ▼ 本地 FastAPI 服务 :8000 │ ▼ Microsoft MarkItDown │ ▼ 返回 Markdown 文本为什么要加 cpolar因为转换服务跑在本地电脑上默认只能本机访问。cpolar 可以把本地8000端口临时映射成一个公网 HTTPS 地址远程同事不用 VPN、不用进内网就能通过接口上传文件。但注意这类服务处理的往往是文档原文通常包含敏感信息。公网暴露必须加鉴权并且建议只临时开放。2. 准备 Python 环境MarkItDown 要求 Python 3.10 或更高版本。先创建项目目录mkdir markitdown-api cd markitdown-api创建并激活虚拟环境python3 -m venv .venv source .venv/bin/activateWindows PowerShell 可以用python -m venv .venv .\.venv\Scripts\Activate.ps1安装依赖pip install -U pip pip install markitdown[all] fastapi uvicorn python-multipart如果只想先支持常见 Office 和 PDF也可以按需安装 MarkItDown 的可选依赖例如pip install markitdown[pdf,docx,pptx,xlsx] fastapi uvicorn python-multipart本文为了减少格式缺依赖导致的转换失败示例使用pip install markitdown[all] fastapi uvicorn python-multipart3. 先确认 MarkItDown 本身可用写 API 之前建议先用命令行确认 MarkItDown 能正常转换文件。准备一个测试文件比如test.docx test.pdf test.pptx执行markitdown test.docx -o output.md或者把结果直接输出到终端markitdown test.pdf如果这一步能得到 Markdown 内容再继续封装 API。这样排查问题会更简单命令行都失败优先检查文件格式、Python 版本、依赖安装命令行成功但 API 失败再检查上传文件保存、路径、权限等问题。4. 编写 FastAPI 转换服务在项目根目录创建main.pyfrom pathlib import Path from tempfile import TemporaryDirectory from uuid import uuid4 from fastapi import FastAPI, File, Header, HTTPException, UploadFile from fastapi.responses import PlainTextResponse from markitdown import MarkItDown app FastAPI(titleLocal MarkItDown API) # 简单 API Token。生产环境建议改成环境变量或更完整的鉴权方案。 API_TOKEN change-this-token # 单文件大小限制20MB可按机器性能调整。 MAX_FILE_SIZE 20 * 1024 * 1024 # 允许的扩展名。按需增减避免把未知文件直接丢给转换器。 ALLOWED_SUFFIXES { .pdf, .docx, .pptx, .xlsx, .xls, .csv, .json, .xml, .html, .htm, .txt, .md, } # 官方 Python APIMarkItDown(enable_pluginsFalse).convert(path).text_content md_converter MarkItDown(enable_pluginsFalse) def check_token(x_api_token: str | None) - None: if x_api_token ! API_TOKEN: raise HTTPException(status_code401, detailInvalid API token) app.get(/health) def health(): return {status: ok} app.post(/convert, response_classPlainTextResponse) async def convert_file( file: UploadFile File(...), x_api_token: str | None Header(defaultNone), ): check_token(x_api_token) original_name file.filename or upload.bin suffix Path(original_name).suffix.lower() if suffix not in ALLOWED_SUFFIXES: raise HTTPException( status_code400, detailfUnsupported file type: {suffix or no suffix}, ) content await file.read() if len(content) MAX_FILE_SIZE: raise HTTPException(status_code413, detailFile too large) with TemporaryDirectory() as tmpdir: safe_name f{uuid4().hex}{suffix} input_path Path(tmpdir) / safe_name input_path.write_bytes(content) try: result md_converter.convert(str(input_path)) except Exception as exc: raise HTTPException( status_code500, detailfConvert failed: {type(exc).__name__}: {exc}, ) from exc return result.text_content这里用了 MarkItDown README 中给出的 Python APIfrom markitdown import MarkItDown md MarkItDown(enable_pluginsFalse) result md.convert(test.xlsx) print(result.text_content)所以在 FastAPI 中我们先把上传文件保存到临时目录再调用result md_converter.convert(str(input_path)) markdown result.text_content这样比直接猜测内部方法更稳也方便和命令行行为对齐。5. 启动本地 API 服务启动服务uvicorn main:app --host 0.0.0.0 --port 8000看到类似输出即可Uvicorn running on http://0.0.0.0:8000先检查健康接口curl http://127.0.0.1:8000/health预期返回{status:ok}6. 本地测试 /convert 接口准备一个测试文档例如demo.docx然后执行curl -X POST http://127.0.0.1:8000/convert \ -H X-API-Token: change-this-token \ -F filedemo.docx \ -o demo.md查看转换结果head -40 demo.md如果想直接在终端查看curl -X POST http://127.0.0.1:8000/convert \ -H X-API-Token: change-this-token \ -F filedemo.pdf测试错误 Tokencurl -i -X POST http://127.0.0.1:8000/convert \ -H X-API-Token: wrong-token \ -F filedemo.docx应该返回401。测试不支持的文件类型curl -i -X POST http://127.0.0.1:8000/convert \ -H X-API-Token: change-this-token \ -F filedemo.exe应该返回400。7. 用 cpolar 暴露本地 8000 端口本地 API 确认没问题后再用 cpolar 暴露cpolar http 8000启动后cpolar 会生成一个公网访问地址形式类似https://xxxx.cpolar.top假设你的公网地址是https://abcd-1234.cpolar.top远程同事就可以这样调用curl -X POST https://abcd-1234.cpolar.top/convert \ -H X-API-Token: change-this-token \ -F filedemo.docx \ -o demo.md自动化流程也可以直接接入这个接口。例如 Python 脚本import requests url https://abcd-1234.cpolar.top/convert headers {X-API-Token: change-this-token} with open(demo.pdf, rb) as f: files {file: (demo.pdf, f, application/pdf)} resp requests.post(url, headersheaders, filesfiles, timeout120) resp.raise_for_status() with open(demo.md, w, encodingutf-8) as f: f.write(resp.text)这样就能把“本地文档转换能力”临时变成一个远程可调用 API。8. 推荐的项目结构最终目录可以这样放markitdown-api/ ├── .venv/ ├── main.py ├── demo.docx └── demo.md如果后续要长期使用建议再补一个requirements.txtpip freeze requirements.txt以后在新机器上恢复环境python3 -m venv .venv source .venv/bin/activate pip install -r requirements.txt9. 常见问题处理9.1 上传接口提示 422FastAPI 文件上传依赖python-multipart。确认已经安装pip install python-multipart接口参数要用-F filedemo.docx不要把文件当 JSON 传。9.2 转换出来的 Markdown 内容不完整MarkItDown 更适合把文档转成给 LLM、文本分析、自动化流程使用的 Markdown不是高保真排版还原工具。如果源文件里有复杂排版、扫描图片、嵌入对象转换结果需要人工检查。建议先用简单 DOCX/PDF 做通路测试对扫描 PDF确认 OCR 相关依赖和识别效果对重要文件保留原文件和转换后的 Markdown 供人工核对。9.3 cpolar 地址能打开但接口返回 401检查请求头是否带了-H X-API-Token: change-this-token也建议把main.py里的 Token 改成更复杂的值例如API_TOKEN mdit-2026-your-random-token更进一步可以改成从环境变量读取import os API_TOKEN os.environ.get(MARKITDOWN_API_TOKEN, change-this-token)启动前设置export MARKITDOWN_API_TOKENmdit-2026-your-random-token uvicorn main:app --host 0.0.0.0 --port 80009.4 大文件转换很慢或失败本文示例限制单文件 20MBMAX_FILE_SIZE 20 * 1024 * 1024如果机器性能有限不建议直接放开限制。文档转换会占 CPU、内存和磁盘临时空间。尤其是大型 PDF、PPT、Excel转换耗时会明显增加。更稳的做法限制文件大小限制允许的文件类型只在需要时打开 cpolar转换完成后关闭服务或换新 Token。10. 安全建议不要把它当成无门槛公网服务这个方案的重点是“临时暴露本地能力”不是把文档转换接口长期裸奔在公网。至少要做到必须加鉴权示例里用了X-API-Token不要删除。只临时开放 cpolar用完就停止cpolar http 8000不要长期挂着。限制文件大小和类型不要允许任意文件上传。谨慎处理敏感文档合同、客户资料、财务文件、内部方案不建议随便通过公网接口传输。不要使用高权限目录处理上传文件示例使用临时目录并且文件名用 UUID 重写避免路径穿越和覆盖本地文件。注意 MarkItDown 的 I/O 权限MarkItDown 官方也提醒转换器会以当前进程权限访问资源。处理不可信输入时应该限制输入来源、文件类型和运行权限。11. 小结这套方案把文档转换拆成了三个清晰步骤MarkItDown 负责转换 FastAPI 负责上传接口 cpolar 负责临时公网访问本地使用时访问http://127.0.0.1:8000/convert远程临时使用时访问https://你的cpolar地址/convert对团队协作来说它的价值不是“又搭了一个服务”而是把原本手动、零散的文档处理动作变成一个可复用的 API远程同事、脚本、自动化流程都能按同一套方式上传文件并拿回 Markdown。如果只是临时协作开 FastAPI cpolar 就够了如果要长期使用再考虑队列、日志、权限系统、对象存储和更完整的审计能力。