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

Python从生成的Excel工作簿转PDF失败:无法保存PDF文档

问题:Excel转PDF时提示“文档未保存,可能已打开或保存时出错”

我需要从现有.xlsx工作簿生成PDF:先通过openpyxl修改工作簿(添加图片),保存到临时路径,再用win32com调用Excel将临时文件转存为PDF。但执行PDF导出时出现错误:

Błąd: (-2147352567, 'Wystąpił wyjątek.', (0, 'Microsoft Excel', 'Nie zapisano dokumentu. Być może jest on otwarty lub przy zapisywaniu napotkano błąd.', 'xlmain11.chm', 0, -2146827284), None)

单独生成.xlsx文件正常,尝试调用wb.close()无效,openpyxl没有直接转PDF的方法。


问题原因分析

  1. tkinter返回的PDF文件对象未关闭,占用目标路径导致Excel无法写入
  2. openpyxl保存临时文件后未彻底释放文件句柄,导致Excel打开时文件被锁定
  3. win32com操作Excel后未关闭工作簿和退出进程,残留进程占用文件
  4. 原codeToPictogram函数逻辑错误,仅遍历第一层目录就返回结果,会漏检子目录中的图片

修复步骤及代码

1. 关闭tkinter的文件对象

asksaveasfile返回的是打开状态的文件对象,会占用PDF路径,需先关闭再使用路径:

file = asksaveasfile(defaultextension='.pdf',title = 'Zapisz stan techniczny', filetypes=[("Plik PDF", "*.pdf")])
if file is not None:
    pdf_path = file.name  # 先保存目标路径
    file.close()  # 立即关闭文件对象释放占用

2. 用with语句管理openpyxl工作簿

with语句会自动释放工作簿资源,避免文件句柄残留:

with openpyxl.load_workbook(initialFile) as wb:
    # 执行所有工作簿修改操作
    wb.save(temp_xlsx_path)
# with块结束后自动关闭wb,释放文件

3. 正确关闭win32com的Excel进程

操作完成后必须关闭工作簿并退出Excel,避免残留进程占用文件:

excel = client.Dispatch("Excel.Application")
excel.Visible = False  # 后台运行不显示窗口
try:
    sheets = excel.Workbooks.Open(temp_xlsx_path)
    work_sheets = sheets.Worksheets[0]
    work_sheets.ExportAsFixedFormat(0, pdf_path)
finally:
    sheets.Close(SaveChanges=False)  # 关闭工作簿,不保存更改
    excel.Quit()  # 退出Excel进程
    del work_sheets, sheets, excel  # 释放COM对象

4. 修复图片检测函数逻辑

原函数仅遍历第一层目录就返回,改为遍历所有目录后再判断:

def codeToPictogram(code):
    target_file = f'{code}.png'
    for root, dirs, files in os.walk(assetsCatalog):
        if target_file in files:
            return 1
    return 0  # 遍历完所有目录未找到才返回0

修复后的完整代码

import sys
import openpyxl
from openpyxl.drawing.image import Image
from openpyxl.utils import get_column_letter
from tkinter.filedialog import asksaveasfile
import os
from win32com import client

initialFile = sys.argv[1]
initialCatalog = sys.argv[2]
assetsCatalog = f"{initialCatalog}\\api\\assets"

def codeToPictogram(code):
    target_file = f'{code}.png'
    for root, dirs, files in os.walk(assetsCatalog):
        if target_file in files:
            return 1
    return 0

try:
    temp_xlsx_path = f'{initialCatalog}\\api\\temp\\temp.xlsx'
    # 使用with语句自动管理工作簿资源
    with openpyxl.load_workbook(initialFile) as wb:
        ws = wb.active
        ws.title = "Stan techniczny nawierzchni"

        for row in ws.iter_cols(min_row=2, min_col=2, max_row=ws.max_row, max_col=ws.max_column):  
            for cell in row:  
                if cell.value is None:
                    continue
                if codeToPictogram(cell.value) == 1:
                    img_path = os.path.join(assetsCatalog, f"{cell.value}.png")
                    img = Image(img_path)
                    img.height = 100
                    img.width = 100
                    ws.add_image(img, f"{get_column_letter(cell.column)}{cell.row}")
        wb.save(temp_xlsx_path)

    file = asksaveasfile(defaultextension='.pdf', title='Zapisz stan techniczny', filetypes=[("Plik PDF", "*.pdf")])
    if file is not None:
        pdf_path = file.name
        file.close()  # 关闭文件对象释放路径

        excel = client.Dispatch("Excel.Application")
        excel.Visible = False
        try:
            sheets = excel.Workbooks.Open(temp_xlsx_path)
            work_sheets = sheets.Worksheets[0]
            work_sheets.ExportAsFixedFormat(0, pdf_path)
            
            print("Gotowe!")
            input("Wciśnij Enter, aby zamknąć...")
        finally:
            # 确保清理Excel资源
            sheets.Close(SaveChanges=False)
            excel.Quit()
            del work_sheets, sheets, excel

except Exception as e:
    print(f"Błąd: {e}")
    input("Wciśnij Enter, aby zamknąć...")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 05:08:13