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
相关产品推荐
相关产品推荐

