
phpspreadsheet 在读取并重新保存含图片或形状的 excel 文件时,常导致特定工作表(如 sheet2)中的图形元素丢失;根本原因在于其不完全支持 office open xml 中的绘图层(drawingml)结构,且写入过程为“重建式”而非“透传式”。
PHPSpreadsheet 并非“无损编辑器”,而是一个以数据和公式处理为核心的功能库。当它读取 .xlsx 文件时,会解析核心内容(单元格值、样式、公式、工作表结构等),但对 DrawingML(即图片、形状、文本框、图表等可视化对象)的支持非常有限:
- ✅ 支持读取部分嵌入图片(如 Worksheet::getDrawingCollection() 可获取 PhpOffice\PhpSpreadsheet\Worksheet\Drawing 对象);
- ⚠️ 但不支持读取/保存形状(Shapes)、SmartArt、组合对象、带文本框的批注图形、以及跨工作表的绘图容器(如 xl/drawings/drawing2.xml 中的 Sheet2 专属绘图);
- ❌ setOffice2003Compatibility(true) 实际上会进一步禁用现代绘图特性,加剧图形丢失;
- ❌ setPreCalculateFormulas(false) 与图形保存无关,可安全移除。
正确做法:仅在必要时操作图形,且需显式保留
若原始文件中图片位于 Sheet2,应主动遍历并重写该工作表的绘图集合:
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
$spreadsheet = IOFactory::load($origin_file);
$sheet = $spreadsheet->getSheetByName('Sheet2'); // 显式定位目标工作表
// 尝试读取现有图片(注意:仅支持简单图片,不支持形状)
$drawingCollection = $sheet->getDrawingCollection();
foreach ($drawingCollection as $drawing) {
if ($drawing instanceof Drawing) {
// 可选:调整位置或大小后重新注册(否则可能因坐标偏移失效)
$drawing->setWorksheet($sheet); // 确保归属正确
}
}
// 保存 —— 移除兼容性模式,启用完整 Open XML 支持
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
$writer->setPreCalculateFormulas(false); // 可保留,但非必需
// ❌ 删除这一行:$writer->setOffice2003Compatibility(true);
$writer->save($new_file);关键注意事项
-
形状(Shapes)无法通过 PHPExcel/PHPSpreadsheet 保留:包括自选图形、流程图、线条、艺术字等均属于 xl/drawings/drawing*.xml 中的
或 元素,当前 PHPSpreadsheet(v2.2+)完全不解析也不写入此类节点; -
替代方案建议:
- 若需严格保真,改用 Apache POI(Java) 或 openpyxl(Python)(后者对绘图支持更完善);
- 或采用「模板填充」策略:将图片/形状预置于 Excel 模板中,仅用 PHPSpreadsheet 填充数据,避免调用 save() 触发重写;
- 验证方式:解压 .xlsx 为 ZIP,对比 xl/drawings/ 目录下 drawing1.xml(Sheet1)与 drawing2.xml(Sheet2)是否在新文件中被生成——若缺失,即确认为库限制。
总之,这不是代码疏漏,而是 PHPSpreadsheet 的设计边界。面对图形密集型报表,请优先评估工具适用性,而非强行适配。











