PHPExcel очень медленный - способы улучшения?
Я создаю отчеты в .xlsx, используя PHPExcel. Это было нормально на начальных этапах тестирования с небольшими наборами данных (десятки строк, 3 листа), но теперь, когда он использует его на реальных производственных данных с более чем 500 рядами на каждом листе, он становится невероятно медленным. 48 секунд, чтобы сгенерировать файл, и при запуске отчета, который объединяет больше информации, все это не работает с Fatal error: Maximum execution time of 30 seconds exceeded in PHPExcel/Worksheet.php on line 1041
. Иногда это в другом файле PHPExcel, поэтому я сомневаюсь, что точное местоположение релевантно.
В идеале, я бы хотел как-то ускорить его, если это возможно. Если нет, то, по крайней мере, увеличьте предел выполнения для этого script.
Единственные предложения, которые я видел до сих пор, - это стиль в диапазонах вместо отдельных ячеек. К сожалению, я уже делаю свой стиль в диапазонах, и он тоже минимален. Любые другие предложения?
Ответы
Ответ 1
Заполняет ли он рабочий лист? или сохранения? что вы находите слишком медленным?
Как вы заполняете таблицу данными?
- Использование
fromArray()
более эффективно, чем fromArray()
каждой отдельной ячейки, особенно если вы используете расширенное связывание значений, чтобы автоматически устанавливать типы данных ячеек. -
Если вы устанавливаете значения для каждой отдельной ячейки в листе, используя
$objPHPExcel->getActiveSheet()->setCellValue('A1',$x);
$objPHPExcel->getActiveSheet()->setCellValue('B1',$y);
использование
$sheet = $objPHPExcel->getActiveSheet();
$sheet->setCellValue('A1',$x);
$sheet->setCellValue('B1',$y);
так что вы getActiveSheet()
доступ только к getActiveSheet()
; или воспользоваться свободным интерфейсом для установки нескольких ячеек только с одним вызовом $objPHPExcel->getActiveSheet()
$objPHPExcel->getActiveSheet()->setCellValue('A1',$x)
->setCellValue('B1',$y);
Вы прокомментировали применение стилей к диапазонам ячеек:
- У вас также есть возможность использовать
applyFromArray()
чтобы установить целый ряд настроек стиля за один раз. - Это намного эффективнее, если вы можете применять стили к столбцу или строке, а не просто к диапазону
Если вы используете формулы в своей книге, при сохранении:
Это всего лишь несколько советов, которые помогут повысить производительность, и в форумах есть еще много предложений. Они не все обязательно помогут, слишком многое зависит от вашей конкретной книги, чтобы дать какие-либо абсолюты, но вы должны иметь возможность улучшить эту медленную скорость. Даже маленький ноутбук, который я использую для разработки, может написать 3 листа, 20 столбцов, 2000 строк Excel 2007 быстрее, чем ваш производственный сервер.
РЕДАКТИРОВАТЬ
Если бы можно было просто улучшить скорость PHPExcel, я бы давно это сделал. Как бы то ни было, я постоянно тестирую производительность, чтобы увидеть, как его скорость может быть улучшена. Если вам нужны более быстрые скорости, чем может дать PHPExcel, то здесь есть список альтернативных библиотек.
Ответ 2
Я столкнулся с этой проблемой. Думал, что я брошу свои два цента, так как этот вопрос получает столько просмотров.
Установка значений ячейки
Вместо того, чтобы устанавливать значение для каждой ячейки отдельно, используйте метод fromArray()
. Взято и изменено из вики.
$arrayData = array(
array(NULL, 2010, 2011, 2012),
array('Q1', 12, 15, 21),
array('Q2', 56, 73, 86),
array('Q3', 52, 61, 69),
array('Q4', 30, 32, 0),
);
$as = $objPHPExcel->getActiveSheet();
$as->fromArray(
$arrayData, // The data to set
NULL, // Array values with this value will not be set
'C3' // Top left coordinate of the worksheet range where
// we want to set these values (default is A1)
);
Элементы для стилизации
Static
Быстрее применять стили для диапазона, чем устанавливать стиль для каждой ячейки отдельно (замечая шаблон?).
$default_style = array(
'font' => array(
'name' => 'Verdana',
'color' => array('rgb' => '000000'),
'size' => 11
),
'alignment' => array(
'horizontal' => \PHPExcel_Style_Alignment::HORIZONTAL_CENTER,
'vertical' => \PHPExcel_Style_Alignment::VERTICAL_CENTER
),
'borders' => array(
'allborders' => array(
'style' => \PHPExcel_Style_Border::BORDER_THIN,
'color' => array('rgb' => 'AAAAAA')
)
)
);
// Apply default style to whole sheet
$as->getDefaultStyle()->applyFromArray($default_style);
$titles = array(
'Name',
'Number',
'Address',
'Telephone'
);
$title_style = array(
'font' => array(
'bold' => true
),
'fill' => array(
'type' => \PHPExcel_Style_Fill::FILL_SOLID,
'startcolor' => array('rgb' => '5CACEE')
),
'alignment' => array(
'wrap' => true
)
);
$as->fromArray($titles, null, 'A1'); // Add titles
$last_col = $as->getHighestColumn(); // Get last column, as a letter
// Apply title style to titles
$as->getStyle('A1:'.$last_col.'1')->applyFromArray($title_style);
Динамически
Я использую PHPExcel для проверки данных, данных в электронной таблице, с текущими данными в базе данных. Поскольку каждая ячейка проверяется индивидуально, я помещаю стили в массив (нуль без стиля) и использовал цикл ниже, чтобы получить диапазон ячеек, чтобы применить стиль к.
/*
* $row is previously set in a loop iterating through each
* row from the DB, which is equal to a spreadsheet row.
* $styles = array(0 => 'error', 1 => 'error', 2 => null, 3 => 'changed', ...);
*/
$start = $end = $style = null;
foreach ($styles as $col => $s) {
if (!$style && !$s) continue;
if ($style === $s) {
$end = $col;
} else {
if ($style) {
$array = null;
switch ($style) {
case 'changed':
$array = $this->changed_style;
break;
case 'error':
$array = $this->error_style;
break;
case 'ignored':
$array = $this->ignored_style;
break;
}
if ($array) {
$start = \PHPExcel_Cell::stringFromColumnIndex($start);
$end = \PHPExcel_Cell::stringFromColumnIndex($end);
$as->getStyle($start.$row.':'.$end.$row)->applyFromArray($array);
}
}
$start = $end = $col;
$style = $s;
}
}
Ответ 3
Я столкнулся с той же проблемой - было около 450 строк с 11 столбцами данных, которые я пытался написать, и я продолжал работать против 30-секундного таймаута. Я смог получить время выполнения до 2 секунд или меньше, добавив все мои новые строки в массе, а затем просмотрел и установил содержимое ячейки после факта. Другими словами, я вставляю 450 строк в один вызов insertNewRowBefore(), а затем прокручиваю и устанавливаю контент в этих строках позже.
Так же:
$num_rows = count($output_rows);
$last_row = $sheet->getHighestRow();
$row = $last_row + 1;
$sheet->insertNewRowBefore($row, $num_rows);
// Now add all of the rows to the spreadsheet
foreach($output_rows as $line) {
$i = 0;
foreach($line as $val) {
// Do your setCellValue() or setCellValueByColumnAndRow() here
$i++;
}
$row++;
}
Ответ 4
Я ни в коем случае не специалист по использованию PHPExcel, но формат OfficeOpenXML (формат *.xlsx файлов) сам по себе представляет собой группу файлов XML, упакованных в ZIP-архив с расширением *.xlsx, Если вы оцениваете свою производительность и знаете, какие данные вы будете передавать, может быть, лучше создать встроенный собственный генератор XLSX, разделенный до наиболее важных функций, возможно, сделать некоторые вычисления на уровне базы данных и т.д. вместо разбора всего документа.
Для этого вы можете начать с анализа файлов, сгенерированных с использованием меньших наборов данных (путем изменения расширения из *.xlsx в *.zip, распаковки и просмотра содержимого отдельных файлов). Таким образом, вы можете определить, что вам действительно нужно, и создать его самостоятельно (создав соответствующие файлы XML и упаковывая их в ZIP-архив, а затем переименовав расширение *.xlsx).
Существует также спецификация OfficeOpenXML, которая является большой (несколько тысяч страниц), поэтому я не предлагаю ее читать, если вы действительно хотеть. Создание файлов в соответствии с тем, как они были созданы PHPExcel, должно быть достаточно.
Решение, упомянутое выше, не содержит ссылок на PHPExcel, потому что я не эксперт в этом. Тем не менее, я ранее интересовался процессом разработки OOXML и был бы рад, если бы знания об этом стандарте помогли бы вам решить вашу проблему.
Ответ 5
Для экспорта XLSX с столбцами a-amj (~ 800) и только ~ 50 строк я также столкнулся с 30-секундной границей. Чтобы проверить мою программу, я ограничил количество обработанных строк до 7, которые работали через 25 секунд.
-
переход от отдельного $objPHPExcel- > getActiveSheet() к $sheet (первый совет), он фактически увеличил время на ограниченном количестве строк с 25 секунд до 26 секунд.
-
То, что действительно помогло мне, заменило всю мою getHighestDataColumn() простой переменной $column_nr, которая была увеличена в PHP, я пошел от 26 секунд до 7 секунд.
После этого я смог обработать все 50 строк за 11 секунд.
Ответ 6
У меня была такая же проблема. Получил 5000 строк, 32 столбца CSV файла, который потребовался навсегда для обработки. Получается, что почти все время "обработка" - это фактически кодировка символов, которая по умолчанию кодирует все UTF8. Поэтому, если вы заходите в свой файл config\excel.php и прокрутите вниз до кодировки, просто установите его как:
/*
|--------------------------------------------------------------------------
| Import encoding
|--------------------------------------------------------------------------
*/
'encoding' => array(
'input' => '',
'output' => ''
),
Только с этим - вышеупомянутый файл занимает около 8 секунд для обработки. Возможно, вы захотите предупредить своего клиента, чтобы он правильно сохранил CSV.
Ответ 7
В моем случае я увеличил производительность, изменив метод хранения кеша в память. gzip cache_in_memory_gzip
$cm = \PHPExcel_CachedObjectStorageFactory::cache_in_memory_gzip;
\PHPExcel_Settings::setCacheStorageMethod($cm);