Переполнение памяти при сохранении Excel-документа
Решаю такую задачу, как сохранение больших данных в Excel-документ. Есть консольная команда, которая из базы выгружает данные в документ формата xlsx.
Код выполняет следующие действия:
- Выделяет память на процесс.
- Создает объект Spreadsheet для заголовка и заполняет электронную таблицу заголовками.
- Далее в цикле создает временные Spreadsheet1 и пакетами по 500 записей дергаем из базы данных и записываем в тот же документ.
- Когда начинается новая итерация цикла while, уничтожаем Spreadsheet1.
- По итогу и на промежуточных этапах сохраняем документ.
В результате возникает ошибка:
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
→ Ссылка
Ответ оказался простым. В документе экселя оказались ссылки, которые были недопустимы для сохранения документа. Потому их пришлось обработать. Тогда все заработало корректно.