web-dev-qa-db-ja.com

PHPExcelは非常に遅い-改善する方法は?

PHPExcelを使用して.xlsxでレポートを生成しています。小さいデータセット(10行、3シート)を使用した初期テスト段階では問題ありませんでしたが、各シートに500行を超える実際の実稼働データで使用すると、非常に遅くなります。ファイルを生成するのに48秒かかり、より多くの情報を組み合わせたレポートを実行すると、すべてがFatal error: Maximum execution time of 30 seconds exceeded in PHPExcel/Worksheet.php on line 1041で失敗します。別のPHPExcelファイルにあることもあるので、正確な場所がその関連性があるとは思いません。

できれば、どうにかして速度を上げたいと思います。そうでない場合は、少なくともこのスクリプトの実行制限を増やします。

これまで見てきた唯一の提案は、個々のセルではなく範囲でスタイルを設定することでした。残念ながら、私はすでに範囲内でスタイリングを行っており、それもかなり最小限です。他の提案はありますか?

43
SaltyNuts

ワークシートに入力されていますか?または保存しますか?遅すぎると思う?

スプレッドシートにデータをどのように入力していますか?

  • fromArray()メソッドを使用すると、特にAdvanced Value Binderを使用してセルのデータ型を自動的に設定する場合に、個々のセルにデータを入力するよりも効率的です。
  • シートの個々のセルごとに値を設定している場合

    _$objPHPExcel->getActiveSheet()->setCellValue('A1',$x);
    $objPHPExcel->getActiveSheet()->setCellValue('B1',$y);
    _

    使用する

    _$sheet = $objPHPExcel->getActiveSheet();
    $sheet->setCellValue('A1',$x);
    $sheet->setCellValue('B1',$y);
    _

    getActiveSheet()メソッドに一度だけアクセスするように。または、流れるようなインターフェイスを利用して、$objPHPExcel->getActiveSheet()を1回呼び出すだけで複数のセルを設定できます

    _$objPHPExcel->getActiveSheet()->setCellValue('A1',$x)
                                  ->setCellValue('B1',$y);
    _

セルの範囲にスタイルを適用することについてコメントしました:

  • applyFromArray()を使用して、さまざまなスタイル設定を一度に設定するオプションもあります。
  • 単純に範囲ではなく列または行にスタイルを適用できると、はるかに効率的です

ワークブックで数式を使用している場合、保存するとき:

  • 使用する

    _$objWriter->setPreCalculateFormulas(false)
    _

    pHPExcel自体の式の計算を無効にします。

これらはパフォーマンスを向上させるためのいくつかのヒントに過ぎず、フォーラムのスレッドにはさらに多くの提案があります。それらはすべて助けになるとは限りません。絶対値を与えるには特定のワークブックに依存しすぎますが、その遅い速度を改善できるはずです。私が開発に使用する小さなノートブックでも、実稼働サーバーよりも高速に3ワークシート、20列、2,000行のExcel 2007ファイルを作成できます。

[〜#〜] edit [〜#〜]

PHPExcel自体の速度を単純に改善することができたなら、私はずっと前にやっていただろう。現状では、速度を改善する方法を確認するために、常にパフォーマンステストを行っています。 PHPExcel自体が提供できる速度よりも高速にしたい場合は、 代替ライブラリのリスト があります。

57
Mark Baker

私もこの問題に遭遇しました。この質問には非常に多くの意見があるので、私は2セントを投じると思いました。

セル値の設定

各セルの値を個別に設定する代わりに、fromArray()メソッドを使用します。 wiki から取得および変更。

$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)
);

セルのスタイリング

静的

また、各セルのスタイルを個別に設定するよりも、範囲にスタイルを適用する方が簡単です(パターンに気づく??)。

$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を使用して、スプレッドシートで指定されたデータをデータベースの現在のデータと確認します。各セルは個別にチェックされるため、スタイルを配列(スタイルなしの場合は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;
    }
} 
13
GreeKatrina

私は同じ問題に直面していました-私が書き込もうとしていた11列のデータで約450行があり、30秒のタイムアウトに対して走り続けました。すべての新しい行を一括して追加し、事後にセルの内容を設定することで、実行時間を2秒以下に短縮することができました。つまり、insertNewRowBefore()の1回の呼び出しで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++;
}
5
JaredC

私は決してPHPExcelの使用の専門家ではありませんが、OfficeOpenXML形式(* .xlsxファイルの形式)自体は、*。xlsx拡張子。パフォーマンスを評価し、渡すデータの種類を知っている場合は、XLSXジェネレーターを構築することをお勧めします。最も重要な機能は、ドキュメント全体を解析する代わりに、データベース層などで計算を行うことです。

これを行うには、小さなデータセットを使用して生成されたファイルの分析から始めます(拡張子を* .xlsxから* .Zipに変更し、展開して、単一ファイルのコンテンツを参照します)。そうすれば、本当に必要なものを判断して自分で生成することができます(適切なXMLファイルを作成してZipアーカイブにパックし、*。xlsx拡張子に名前を変更します)。

OfficeOpenXML の仕様もあり、これは大きい(数千ページ)ため、本当に必要でない限り読むことを提案しません。 PHPExcelで生成された方法と一致するファイルを作成すれば十分です。

上記のソリューションには、PHPExcelに関連するヒントは含まれていません。これは、私がエキスパートではないためです。ただし、以前はOOXMLの標準化プロセスに興味があり、この標準に関する知識が問題の解決に役立つなら幸いです。

2
Tadeck

列a-amj(〜800)とわずか50行のXLSXエクスポートの場合、30秒の境界に遭遇しました。プログラムをテストするために、処理する行の量をわずか7に制限し、25秒で機能しました。

  1. 個々の$ objPHPExcel-> getActiveSheet()から$ sheet(最初のアドバイス)に行くと、実際には限られた行の時間を25秒から26秒に増やしました。

  2. 私が本当に助けたのは、getHighestDataColumn()をすべてPHPでインクリメントされる単純な$ column_nr変数に置き換えることで、26秒から7秒になりました。

その後、11行で50行すべてを処理できました。

1
Cas Tuyn

私の場合、キャッシュストレージメソッドをメモリ内gzipに変更することでパフォーマンスを向上させましたcache_in_memory_gzip

$cm = \PHPExcel_CachedObjectStorageFactory::cache_in_memory_gzip;
\PHPExcel_Settings::setCacheStorageMethod($cm);
0
Farid Movsumov

私はまったく同じ問題を抱えていました。処理に永遠にかかった5000行、32列のCSVファイルを取得しました。 「処理」に費やされるほとんどすべての時間は、実際にはデフォルトですべてをUTF8にエンコードするように設定された文字エンコードであることがわかります。したがって、config\Excel.phpファイルに移動してエンコードまでスクロールダウンする場合は、次のように設定します。

/*
|--------------------------------------------------------------------------
| Import encoding
|--------------------------------------------------------------------------
*/
    'encoding' => array(

        'input'  => '',
        'output' => ''

    ),

これだけで-上記のファイルの処理には約8秒かかります。ただし、CSVを正しく保存するようにクライアントに警告することもできます。

0
Skipp