0

0

如何高效在 PhpSpreadsheet 中批量插入图片避免内存溢出

霞舞

霞舞

发布时间:2026-01-23 22:25:01

|

306人浏览过

|

来源于php中文网

原创

如何高效在 PhpSpreadsheet 中批量插入图片避免内存溢出

使用 phpspreadsheet 的 `memorydrawing` 批量写入数千张缩略图时易触发内存溢出;本文提供基于磁盘持久化的分块写入方案,配合 gd 资源及时释放与工作表复用技巧,实现在低内存占用下稳定导出含图 excel 文件。

在使用 PhpSpreadsheet 导出含大量图片(如 6000+ 商品缩略图)的 Excel 文件时,直接循环创建 MemoryDrawing 对象极易导致 PHP 进程内存耗尽——根本原因在于:MemoryDrawing 会将 GD 图像资源、图像二进制数据及绘图元信息全部保留在内存中,且 PhpSpreadsheet 内部未自动释放已写入的图像资源,累积效应显著。

核心优化原则

  • 避免全量驻留内存:不等待全部图片写完再保存,而是采用「分块写入 + 磁盘落盘」策略;
  • 严格释放 GD 资源:每轮处理后显式调用 imagedestroy();
  • 禁用冗余对象引用:unset($drawing) 仅销毁变量,必须配合 imagedestroy() 才能真正释放图像内存;
  • 规避重载破坏:不要用 Reader→load() 重载已写入图片的文件(会导致原图丢失),而应始终追加写入或分段生成独立 sheet。

以下是经过生产验证的稳健实现方案:

use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;

$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();

// 设置列宽自适应
$sheet->getColumnDimension('B')->setAutoSize(true);
$sheet->getColumnDimension('C')->setAutoSize(true);

$batchSize = 200; // 每 200 行保存一次
$filePath = 'products_with_thumbnails.xlsx';

foreach ($products as $k => $product) {
    $row = $k + 1;

    // 写入文本字段
    $sheet->setCellValueByColumnAndRow(1, $row, $product['ref']);
    $sheet->getCellByColumnAndRow(1, $row)->setDataType(DataType::TYPE_STRING);
    $sheet->setCellValueByColumnAndRow(2, $row, $product['title']);

    // 插入缩略图(仅当存在时)
    if (!empty($product['image'])) {
        $imagePath = $product['image']; // 假设为本地路径
        $sheet->getRowDimension($row)->setRowHeight(80);

        $drawing = new MemoryDrawing();
        $drawing->setName("Thumbnail_{$row}");
        $drawing->setDescription("Thumbnail for {$product['ref']}");
        $drawing->setResizeProportional(true);
        $drawing->setHeight(80);

        // 安全加载 GD 图像并自动识别格式
        $gdImage = null;
        if (stripos($imagePath, '.png') !== false) {
            $gdImage = @imagecreatefrompng($imagePath);
            $drawing->setRenderingFunction(MemoryDrawing::RENDERING_PNG);
            $drawing->setMimeType(MemoryDrawing::MIMETYPE_PNG);
        } elseif (stripos($imagePath, '.jpg') !== false || stripos($imagePath, '.jpeg') !== false) {
            $gdImage = @imagecreatefromjpeg($imagePath);
            $drawing->setRenderingFunction(MemoryDrawing::RENDERING_JPEG);
            $drawing->setMimeType(MemoryDrawing::MIMETYPE_JPEG);
        }

        if ($gdImage !== false && $gdImage !== null) {
            $drawing->setImageResource($gdImage);
            $drawing->setCoordinates('C' . $row);
            $drawing->setOffsetX(0);
            $drawing->setOffsetY(0);
            $drawing->setWorksheet($sheet);

            // ✅ 关键:立即释放 GD 资源(大幅降低内存峰值)
            imagedestroy($gdImage);
        }
        unset($drawing); // 清理对象引用
    }

    // ✅ 每 batchSize 行保存一次,并刷新内存
    if (($k + 1) % $batchSize === 0 || $k === count($products) - 1) {
        $writer = new Xlsx($spreadsheet);
        $writer->save($filePath);

        // 强制 GC 并断开工作表引用(防止内存残留)
        $spreadsheet->disconnectWorksheets();
        $spreadsheet->garbageCollect();
        unset($spreadsheet, $sheet);

        // 重新初始化(保持文件句柄续写逻辑)
        $spreadsheet = new Spreadsheet();
        $sheet = $spreadsheet->getActiveSheet();
        $sheet->getColumnDimension('B')->setAutoSize(true);
        $sheet->getColumnDimension('C')->setAutoSize(true);
    }
}

echo "✅ 导出完成,文件已保存至: {$filePath}";

⚠️ 重要注意事项

Napkin AI
Napkin AI

Napkin AI 可以将您的文本转换为图表、流程图、信息图、思维导图视觉效果,以便快速有效地分享您的想法。

下载

立即学习PHP免费学习笔记(深入)”;

  • 不要重载 .xlsx 文件:Reader::load() 读取已写入图片的 Excel 时,MemoryDrawing 图像不会被反序列化,因此重载后调用 setWorksheet() 会丢失所有历史图片;分块方案必须通过「新建 Spreadsheet → 追加新数据 → 全量保存」实现,而非「读取 + 修改 + 覆盖保存」;
  • 启用 PHP 内存限制放宽(临时):若仍遇瓶颈,可在脚本开头添加 ini_set('memory_limit', '1G');,但应以优化逻辑为先;
  • 推荐替代方案(超大规模场景):对于 >10,000 行含图导出,建议改用 PhpSpreadsheet 的 Drawing 类配合 setPath() 直接引用本地图片路径(需 Excel 客户端支持),或导出为 HTML 表格 + 内联 Base64 图片后转 Excel,兼顾性能与兼容性。

通过以上结构化分块 + 及时资源释放 + 避免反序列化陷阱,可稳定支撑 5000–8000 行带图导出,内存占用控制在 256–512MB 区间(取决于图片尺寸),彻底解决 Out of memory 报错问题。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
excel对比两列数据异同
excel对比两列数据异同

Excel作为数据的小型载体,在日常工作中经常会遇到需要核对两列数据的情况,本专题为大家提供excel对比两列数据异同相关的文章,大家可以免费体验。

1454

2023.07.25

excel重复项筛选标色
excel重复项筛选标色

excel的重复项筛选标色功能使我们能够快速找到和处理数据中的重复值。本专题为大家提供excel重复项筛选标色的相关的文章、下载、课程内容,供大家免费下载体验。

428

2023.07.31

excel复制表格怎么复制出来和原来一样大
excel复制表格怎么复制出来和原来一样大

本专题为大家带来excel复制表格怎么复制出来和原来一样大相关文章,帮助大家解决问题。

572

2023.08.02

excel表格斜线一分为二
excel表格斜线一分为二

在Excel表格中,我们可以使用斜线将单元格一分为二。本专题为大家带来excel表格斜线一分为二怎么弄的相关文章,希望可以帮到大家。

1264

2023.08.02

excel斜线表头一分为二
excel斜线表头一分为二

excel斜线表头一分为二的方法有使用合并单元格功能方法、使用文本框功能方法、使用自定义格式方法。本专题为大家提供excel斜线表头一分为二相关的各种文章、以及下载和课程。

376

2023.08.02

绝对引用的输入方法
绝对引用的输入方法

绝对引用允许在公式中引用一个固定的单元格,而不会随着公式的复制和粘贴而改变引用的单元格。本专题为大家提供绝对引用相关内容的文章,大家可以免费体验。

4564

2023.08.09

java导出excel
java导出excel

在Java中,我们可以使用Apache POI库来导出Excel文件。本专题提供java导出excel的相关文章,大家可以免费体验。

464

2023.08.18

excel输入值非法
excel输入值非法

在Excel中,当输入的数值非法时,有以下多种处理方法。本专题为大家提供excel输入值非法的相关文章,大家可以免费体验。

1034

2023.08.18

TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

26

2026.03.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PHP课程
PHP课程

共137课时 | 13.5万人学习

JavaScript ES5基础线上课程教学
JavaScript ES5基础线上课程教学

共6课时 | 11.3万人学习

PHP新手语法线上课程教学
PHP新手语法线上课程教学

共13课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号