Python openpyxl绘制Excel像素画 大图生成文件损坏问题求助
Excel逐像素绘图大尺寸文件损坏问题排查与解决
问题描述
基于openpyxl+PIL编写的自动化脚本,可在Microsoft Excel中通过逐单元格填色实现像素画效果:
- 处理500×500左右小尺寸图片时运行正常,输出文件可正常打开
- 传入大尺寸图片时,生成的Excel文件提示损坏,无法正常打开
- 脚本内变量为波兰语命名,完整实现代码如下:
import time from openpyxl import Workbook, load_workbook from openpyxl.styles import Color, PatternFill, Font, Border from PIL import Image plik = "1.xlsx" fota = "fb.png" start = time.time() zdj = Image.open(fota) zdj_rgb = zdj.convert("RGB") szer, wys = zdj.size wb = load_workbook(plik) ws = wb.active def rgb_to_hex(rgb): return '%02x%02x%02x' % rgb def excel_col(col): quot, rem = divmod(col-1,26) return excel_col(quot) + chr(rem+ord('A')) if col!=0 else '' for y in range(wys): for x in range(szer): yy = y+1 komorka = str(excel_col(x+1) + str(yy)) pixel_rgb = zdj_rgb.getpixel((x, y)) rgb = "00" + str(rgb_to_hex((pixel_rgb[0], pixel_rgb[1], pixel_rgb[2]))) rgbFill = PatternFill(start_color=rgb, end_color=rgb, fill_type='solid') ws[komorka].fill = rgbFill end = time.time() minuty, sekundy = divmod((end-start), 60) print(f"Renderowanie zajelo {minuty} minut/y i {round(sekundy)} sekund.") wb.save(plik)
排查思路
按优先级从高到低排查以下三类原因:
- 超出Excel格式硬限制:Microsoft Excel单工作表最大支持16384列、1048576行,若输入图片宽度超过16384像素、高度超过1048576像素,脚本会生成超出合法范围的单元格地址,直接破坏xlsx文件结构。
- 样式冗余超限:当前脚本为每个单元格单独创建
PatternFill样式对象,即使多个单元格颜色完全一致也会生成独立样式条目。大尺寸图片下单元格数量暴涨,xlsx内部样式表的条目数会快速超出Excel或openpyxl支持的上限,触发文件损坏。 - 写入不完整:默认模式下openpyxl会将全量工作表数据加载到内存,大尺寸图片对应单元格量达到数十万甚至上百万级别时,保存阶段容易因内存不足出现写入截断,生成不完整的xlsx压缩包,导致打开时提示损坏。
解决方案
对应问题点逐一修复即可:
- 增加尺寸前置校验:加载图片后先判断宽高是否超出Excel单表行列上限,超出则先等比缩放到合法范围再处理,避免生成非法单元格地址。
- 增加样式缓存复用:新建字典做颜色到样式对象的映射,相同RGB颜色仅创建一次
PatternFill对象,所有同色单元格复用该对象,可将样式条目数从“单元格总数”降低到“图片使用的颜色总数”,同时大幅提升运行速度。核心优化代码如下:
# 初始化样式缓存 fill_cache = {} for y in range(wys): for x in range(szer): yy = y + 1 # 直接通过行列号获取单元格,比拼接地址字符串效率高3~5倍 komorka = ws.cell(row=yy, column=x+1) pixel_rgb = zdj_rgb.getpixel((x, y)) rgb_key = "00" + rgb_to_hex(pixel_rgb) # 缓存中无对应颜色时再新建样式 if rgb_key not in fill_cache: fill_cache[rgb_key] = PatternFill(start_color=rgb_key, end_color=rgb_key, fill_type='solid') komorka.fill = fill_cache[rgb_key]
- 大尺寸场景切换为只写模式:当图片尺寸超过2000×2000时,放弃加载现有Excel文件,使用
Workbook(write_only=True)创建只写工作簿,该模式下openpyxl不会将全量数据驻留内存,而是逐段将数据写入磁盘,可彻底避免内存不足导致的写入截断问题。 - 移除低效的列名转换逻辑:原脚本中递归实现的
excel_col函数在列数较大时性能很差,直接用ws.cell(row=行号, column=列号)的方式访问单元格,无需手动拼接列名字符串,可大幅缩短整体运行时间。
内容的提问来源于stack exchange,提问作者Michal Pielka
相关产品推荐
相关产品推荐

