如何用PhpSpreadSheet导出XLSX筛选记录并保留原单元格格式
问题根源
现有代码丢失格式的核心原因:
- 调用
toArray()方法仅提取单元格纯值,字体颜色、背景色、富文本样式、单元格格式等属性全部被丢弃 - 写入新文件时仅用
setCellValue()传纯值,没有同步迁移样式属性,自然无法保留原格式 - 额外逻辑问题:自定义读过滤器写死只读前10行A-R列,文件行数超10行时会漏读;反复做JSON编解码转数组属于冗余操作,会损耗性能还可能导致特殊字符丢失。
实现方案
优先采用加载原文件后直接删除不符合筛选条件行的方案,不需要单独复制值和样式,删除操作会自动保留剩余行的所有原有样式、批注、格式,兼容性最好、实现成本最低。如果需要保留原文件不改动,加载后在内存中操作,最后另存为新文件即可。
修正后可直接运行的代码
<?php require 'vendor/autoload.php'; use PhpOffice\PhpSpreadsheet\Reader\Xlsx; use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter; // 注意这里要填具体的xlsx文件路径,原代码写文件夹路径是错误的 $inputFilePath = '../out/你的源数据文件.xlsx'; $outputFilePath = '../out/筛选后带样式的结果.xlsx'; $filterColumn = 'P'; // 筛选条件所在列 $filterMatchValue = 'Not Relevant'; // 筛选匹配值 // 加载原文件,不需要加自定义读过滤器,避免漏读内容 $reader = new Xlsx(); $spreadsheet = $reader->load($inputFilePath); $sheet = $spreadsheet->getActiveSheet(); $maxRow = $sheet->getHighestRow(); // 获取表格实际最大行数 // 从最后一行往前倒序遍历,删除不符合条件的行 // 倒序删除不会打乱前面未遍历行的行号,避免遍历错位漏判 for ($row = $maxRow; $row >= 2; $row--) { // 第1行是表头,默认保留 $currentCellValue = $sheet->getCell($filterColumn . $row)->getValue(); if ($currentCellValue !== $filterMatchValue) { $sheet->removeRow($row, 1); } } // 如果只需要保留指定列(比如原逻辑只保留A、B列),可按需删除多余列,样式会自动保留 // 示例:保留A、B列的话,从C列开始删除16列,刚好删到R列 // $sheet->removeColumn('C', 16); // 写出文件,所有保留内容的原有颜色、字体等样式会完整留存 $writer = new XlsxWriter($spreadsheet); $writer->save($outputFilePath); // 释放内存 $spreadsheet->disconnectWorksheets(); unset($spreadsheet);
需复制到独立模板的特殊场景处理
如果业务要求必须把筛选结果写入单独的模板文件,不要只用setCellValue()写值,要同步复制样式:
// 替换原有的setCellValue逻辑 // 1. 写入单元格值 $targetCell = $sheet1->getCell('A'.$rcount); $targetCell->setValue($sourceCell->getValue()); // 2. 同步复制原单元格样式 $sheet1->duplicateStyle( $sheet->getStyle('A'.$sourceRowNum), 'A'.$rcount );
如果单元格内是富文本(同一段内容包含多种字体、颜色),优先用前面的删行方案,复制样式的方式对富文本的兼容性不如直接删行的方案。
注意事项
- 不要在读过滤器中限制读取行数、列数,否则超出限制的内容不会被加载,筛选结果会缺失
- 行删除操作必须从最大行往回遍历,从前往后删会导致行号上移,出现漏判
- 不需要把表格数据转数组、反复做JSON编解码,直接操作工作表对象性能更高,也不会出现特殊字符转义丢失的问题
内容的提问来源于stack exchange,提问作者Sunny J
相关产品推荐
相关产品推荐

