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的方法。
问题原因分析
- tkinter返回的PDF文件对象未关闭,占用目标路径导致Excel无法写入
- openpyxl保存临时文件后未彻底释放文件句柄,导致Excel打开时文件被锁定
- win32com操作Excel后未关闭工作簿和退出进程,残留进程占用文件
- 原
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
相关产品推荐
相关产品推荐

