You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask开发docx转pdf程序点击下载触发FileNotFoundError报错排查

Flask docx转pdf下载触发FileNotFoundError问题修复

问题原因

  • 文件名逻辑不一致:转换时硬编码输出文件名为hello.pdf,但构造下载文件名时使用「上传docx的主名+.pdf」的规则,实际生成的文件名和下载时查找的文件名完全不匹配,是触发找不到文件错误的核心原因。
  • 路径管理不规范:上传目录配置为空字符串,文件保存、转换输出、文件下载全依赖相对路径,Flask启动时的工作目录一旦和代码预期不符,即使文件名正确也会出现路径找不到的问题。
  • 文件名传参逻辑冗余:通过把空格替换为=再回退的方式传递文件名,特殊字符场景下极易出现文件名解析错误,进一步提升了文件查找失败的概率。
  • 存在安全隐患:下载接口直接读取前端传入的文件名返回文件,没有做路径校验,容易被恶意构造参数触发路径遍历,读取服务器上的敏感文件。

修复步骤

  • 先在项目根目录创建专门的uploads文件夹,用来统一存放上传的docx和转换生成的pdf,避免相对路径混乱。
  • 统一转换输出的文件名规则,保证生成的pdf文件名和下载时查找的文件名完全一致。
  • 所有文件操作都使用绝对路径拼接,不要依赖运行时的工作目录。
  • 去掉无意义的空格替换传参逻辑,下载时增加路径校验,禁止访问上传目录之外的文件。

修正后完整代码

from flask import Flask
from flask import request, render_template, send_file
import os
from typing import Tuple
from docx2pdf import convert

# 配置上传目录为项目根目录下的uploads文件夹,使用绝对路径避免路径问题
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
UPLOAD_FOLDER = os.path.join(BASE_DIR, 'uploads')
# 目录不存在就自动创建
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

def docx_to_pdf(input_file: str, output_file: str, pages: Tuple = None):
    if pages:
        pages = [int(i) for i in list(pages) if i.isnumeric()]
    convert(input_file, output_file)
    print('转换完成')
    summary = {
        "源文件": input_file, "转换页码": str(pages), "输出文件": output_file
    }
    print("\n".join("{}:{}".format(i, j) for i, j in summary.items()))

@app.route('/', methods=['GET', 'POST'])
@app.route('/index', methods=['GET', 'POST'])
def index():
    if request.method == "POST":
        file = request.files['filename']
        if file.filename != '':
            # 拼接上传docx的绝对保存路径
            docx_save_path = os.path.join(app.config['UPLOAD_FOLDER'], file.filename)
            file.save(docx_save_path)
            # 统一pdf输出路径:和原docx同目录,主名一致,后缀为pdf
            pdf_filename = f"{os.path.splitext(file.filename)[0]}.pdf"
            pdf_save_path = os.path.join(app.config['UPLOAD_FOLDER'], pdf_filename)
            # 执行转换
            docx_to_pdf(docx_save_path, pdf_save_path)
            print(f"生成pdf路径:{pdf_save_path}")
            # 直接传pdf文件名到模板,不需要做空格替换
            return render_template("docx.html", pdf_filename=pdf_filename)
    return render_template("index.html")


@app.route('/docx', methods=['POST'])
def download_docx():
    pdf_filename = request.form.get('filename', None)
    if not pdf_filename:
        return "未指定下载文件", 400
    # 拼接下载文件的绝对路径,同时做安全校验,防止路径遍历
    pdf_path = os.path.abspath(os.path.join(app.config['UPLOAD_FOLDER'], pdf_filename))
    # 校验最终路径必须在上传目录内,避免恶意访问其他文件
    if not pdf_path.startswith(app.config['UPLOAD_FOLDER']):
        return "非法文件请求", 403
    if not os.path.exists(pdf_path):
        return "文件不存在", 404
    return send_file(pdf_path, as_attachment=True)

if __name__=="__main__":
    app.debug = True
    app.run()

额外注意

  • Windows系统运行需要提前安装Microsoft Word,docx2pdf依赖Word组件完成转换;Linux/macOS系统需要提前安装LibreOffice,环境缺失会导致转换失败、pdf文件未生成。
  • 模板部分需要同步调整,docx.html里的下载表单直接传pdf_filename变量即可,不需要做任何字符替换。

内容的提问来源于stack exchange,提问作者Mixhalo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 09:48:52