PHPExcel非常慢 - 改进方法?

Sal*_*uts 43 php spreadsheet phpexcel

我正在使用PHPExcel在.xlsx中生成报告.在初始测试阶段可以使用小数据集(数十行,3张),但现在当在每张表中超过500行的实际生产数据上使用它时,它变得非常慢.48秒生成一个文件,当运行一个结合了更多信息的报表时,整个事情都失败了Fatal error: Maximum execution time of 30 seconds exceeded in PHPExcel/Worksheet.php on line 1041.有时它在另一个PHPExcel文件中,所以我怀疑确切的位置是相关的.

理想情况下,如果可能的话,我想以某种方式加快速度.如果没有,那么至少增加该脚本的执行限制.

到目前为止我看到的唯一建议是在范围而不是单个单元格中设置样式.不幸的是,我已经在范围内做了我的造型,它也是相当小的.还有其他建议吗?

Mar*_*ker 57

是填充工作表吗?还是储蓄?你发现太慢了?

您如何使用数据填充电子表格?

  • 使用此fromArray()方法比填充每个单独的单元格更有效,尤其是在使用"高级值绑定器"自动设置单元格数据类型时.
  • 如果您正在使用工作表中的每个单元格设置值

    $objPHPExcel->getActiveSheet()->setCellValue('A1',$x);
    $objPHPExcel->getActiveSheet()->setCellValue('B1',$y);
    
    Run Code Online (Sandbox Code Playgroud)

    使用

    $sheet = $objPHPExcel->getActiveSheet();
    $sheet->setCellValue('A1',$x);
    $sheet->setCellValue('B1',$y);
    
    Run Code Online (Sandbox Code Playgroud)

    这样你只能访问getActiveSheet()一次方法; 或利用流畅的界面设置多个单元格,只需一次调用即可$objPHPExcel->getActiveSheet()

    $objPHPExcel->getActiveSheet()->setCellValue('A1',$x)
                                  ->setCellValue('B1',$y);
    
    Run Code Online (Sandbox Code Playgroud)

您已经评论过将样式应用于单元格范围:

  • 您还可以选择applyFromArray()一次性设置各种样式设置.
  • 如果您可以将样式应用于列或行而不是简单地应用于范围,则效率会更高

如果您在工作簿中使用公式,则在保存时:

这些只是帮助提升性能的一些提示,并且在论坛主题中还有更多建议.它们不一定都会有所帮助,太多取决于你的具体工作手册给予任何绝对,但你应该能够提高那么慢的速度.即使是我用于开发的小笔记本也可以比生产服务器更快地编写3个工作表,20列,2,000行Excel 2007文件.

编辑

如果可以简单地提高PHPExcel本身的速度,我很久以前就已经这么做了.事实上,我经常进行性能测试,看看它的速度如何提高.如果你想要比PHPExcel本身更快的速度,那么这里有一个替代库列表.

  • @ javiniar.leonard - `insertNewRowBefore()`总是很昂贵,当插入到工作表顶部而不是靠近底部时更是如此:总是尽量避免在循环中使用它; 您可以指定要插入的行数作为第二个参数,这比单独插入每行更有效 (4认同)
  • 有没有人也注意到`$ sheet-> insertNewRowBefore($ row)`单独(在循环内)也会导致一些性能下降? (3认同)

Gre*_*ina 13

我也遇到过这个问题.我以为我会抛出两分钱,因为这个问题得到了很多观点.

设置单元格值

不要单独设置每个单元格的值,而是使用该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)
);
Run Code Online (Sandbox Code Playgroud)

造型细胞

静态的

应用范围的样式也比单独设置每个单元格的样式(注意模式??)更快.

$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);
Run Code Online (Sandbox Code Playgroud)

动态

我使用PHPExcel检查电子表格中给出的数据以及数据库中的当前数据.由于每个单元格都是单独检查的,因此我将样式放在一个数组中(null表示没有样式),并使用下面的循环来获取要应用样式的单元格范围.

/*
 * $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;
    }
} 
Run Code Online (Sandbox Code Playgroud)


Jar*_*edC 5

我遇到了同样的问题 - 有大约450行,我试图写入11列数据,而且我一直在对抗30秒超时.通过批量添加所有新行,然后通过并在事后设置单元格内容,我能够将执行时间缩短到2秒或更短.换句话说,我在insertNewRowBefore()的一次调用中插入450行,然后循环并稍后在这些行中设置内容.

像这样:

$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++;
}
Run Code Online (Sandbox Code Playgroud)

  • 我用`-> insertNewRowBefore()`尝试了您的建议。我要在两张纸上写大约6000行。不使用`-> insertNewRowBefore`的循环`-> setCellValue`花费** 151秒**,而使用`-> insertNewRowBefore`则花费** 147秒**。因此很遗憾,我无法确认您的解决方案:-( (2认同)