Переполнение памяти при сохранении Excel-документа

Решаю такую задачу, как сохранение больших данных в Excel-документ. Есть консольная команда, которая из базы выгружает данные в документ формата xlsx.

Код выполняет следующие действия:

  1. Выделяет память на процесс.
  2. Создает объект Spreadsheet для заголовка и заполняет электронную таблицу заголовками.
  3. Далее в цикле создает временные Spreadsheet1 и пакетами по 500 записей дергаем из базы данных и записываем в тот же документ.
  4. Когда начинается новая итерация цикла while, уничтожаем Spreadsheet1.
  5. По итогу и на промежуточных этапах сохраняем документ.

В результате возникает ошибка:

Worksheet!D301 -> Unable to access External Workbook

на этапе загрузки второго пакета данных. Вот код:

ini_set('memory_limit', '512M');
...
// Создаем новый объект Spreadsheet
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();

 $count_data = DB::table($table_name)->count();

            $data = DB::table($table_name)->get();
            //skip($pos)->take(1000)->

            $headers = [];
            if (count($data) > 0) {
                // Обработчик заголовков
                $this->info('Создание заголовков');
                foreach ($data as $itm) {

                    $Arr = json_decode($itm->data_json);

                    if (is_object($Arr))
                        // Добавляем ключи в массив заголовков
                        foreach ($Arr as $key => $value) {
                            if (!in_array($key, $headers)) {
                                $headers[] = $key;
                            }
                        }
                }

                $rowIndex = 1;
                $columnIndex = 1; // Начинаем с первого столбца
                foreach ($headers as $colIndex => $header) {
                    //$sheet->setCellValueByColumnAndRow($columnIndex++, 1, $header);
                    $colLetter = Coordinate::stringFromColumnIndex($colIndex + 1); // преобразуем номер колонки в букву
                    $cell = $colLetter . $rowIndex;
                    $sheet->setCellValue($cell, $header);
                }

                $this->info('Заголовки созданы...');
                $this->info('Запись данных заголовков документа...');

                try {
                    $writer = new Xlsx($spreadsheet);
                    $writer->save(base_path('public') . '/' . $document);
                } catch (\PhpOffice\PhpSpreadsheet\Writer\Exception $e) {
                    $this->error('Ошибка при сохранении документа: ' . $e->getMessage());
                }

$rowIndex = 2; // Начинаем со второй строки
$pos = 0;    
while ($rowIndex < $count_data) {

   $dump = DB::table($table_name)->skip($pos)->take(500)->get();

if ($dump->count()) {
$spreadsheet1 = \PhpOffice\PhpSpreadsheet\IOFactory::load(base_path('public').'/'.$document);
//$spreadsheet1 = new Spreadsheet();
$sheet1 = $spreadsheet1->getActiveSheet();

foreach ($dump as $item) {
   $Arr = json_decode($item->data_json);
       foreach ($headers as $colIndex => $header) {
           $colLetter = Coordinate::stringFromColumnIndex($colIndex + 1);
           $cell = $colLetter . $rowIndex;
           $sheet1->setCellValue($cell, $Arr->$header ?? '');
       }
       $rowIndex++;
}

$this->info('Сохранение документа ' . base_path('public') . '/' .$document);
$this->info('Использование памяти: ' . memory_get_usage() . ' байт');

try {
      $writer = new Xlsx($spreadsheet1);
      $writer->save(base_path('public') . '/'.$document);
      sleep(5);
      unset($spreadsheet1);
      unset($sheet1);
} catch (\PhpOffice\PhpSpreadsheet\Writer\Exception $e) {
      $this->error('Ошибка при сохранении документа: ' . $e->getMessage());
    }
}

Как решить проблему? Есть идеи?


Ответы (1 шт):

Автор решения: IntegralAL

Ответ оказался простым. В документе экселя оказались ссылки, которые были недопустимы для сохранения документа. Потому их пришлось обработать. Тогда все заработало корректно.

→ Ссылка