Подтвердить что ты не робот

PHPExcel очень медленный - способы улучшения?

Я создаю отчеты в .xlsx, используя PHPExcel. Это было нормально на начальных этапах тестирования с небольшими наборами данных (десятки строк, 3 листа), но теперь, когда он использует его на реальных производственных данных с более чем 500 рядами на каждом листе, он становится невероятно медленным. 48 секунд, чтобы сгенерировать файл, и при запуске отчета, который объединяет больше информации, все это не работает с Fatal error: Maximum execution time of 30 seconds exceeded in PHPExcel/Worksheet.php on line 1041. Иногда это в другом файле PHPExcel, поэтому я сомневаюсь, что точное местоположение релевантно.

В идеале, я бы хотел как-то ускорить его, если это возможно. Если нет, то, по крайней мере, увеличьте предел выполнения для этого script.

Единственные предложения, которые я видел до сих пор, - это стиль в диапазонах вместо отдельных ячеек. К сожалению, я уже делаю свой стиль в диапазонах, и он тоже минимален. Любые другие предложения?

4b9b3361

Ответ 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() чтобы установить целый ряд настроек стиля за один раз.
  • Это намного эффективнее, если вы можете применять стили к столбцу или строке, а не просто к диапазону

Если вы используете формулы в своей книге, при сохранении:

  • использование

    $objWriter->setPreCalculateFormulas(false)
    

    чтобы отключить вычисление формул в самом PHPExcel.

Это всего лишь несколько советов, которые помогут повысить производительность, и в форумах есть еще много предложений. Они не все обязательно помогут, слишком многое зависит от вашей конкретной книги, чтобы дать какие-либо абсолюты, но вы должны иметь возможность улучшить эту медленную скорость. Даже маленький ноутбук, который я использую для разработки, может написать 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);