PHPExcel sehr langsam – Möglichkeiten zur Verbesserung?
Lesezeit: 11 Minuten
Ich erstelle Berichte in .xlsx mit PHPExcel. In den ersten Testphasen mit kleinen Datensätzen (zig Zeilen, 3 Blätter) war es in Ordnung, aber jetzt, wenn es auf echten Produktionsdaten mit über 500 Zeilen in jedem Blatt verwendet wird, wird es unglaublich langsam. 48 Sekunden, um eine Datei zu generieren, und wenn ein Bericht ausgeführt wird, der mehr Informationen kombiniert, schlägt das Ganze fehl Fatal error: Maximum execution time of 30 seconds exceeded in PHPExcel/Worksheet.php on line 1041. Manchmal befindet es sich in einer anderen PHPExcel-Datei, daher bezweifle ich, dass der genaue Speicherort so relevant ist.
Idealerweise würde ich es, wenn möglich, irgendwie beschleunigen wollen. Wenn nicht, erhöhen Sie zumindest das Ausführungslimit für dieses Skript.
Die einzigen Vorschläge, die ich bisher gesehen habe, waren Bereiche statt einzelner Zellen. Leider mache ich mein Styling schon in Ranges und es ist auch eher minimal. Irgendwelche anderen Vorschläge?
Ich verwende diese Bibliothek für einen Bericht, der nur 40 Datenzeilen enthält, aber SEHR stilisiert ist und 2 Seiten breit ist, sodass ich viele Spalten habe. In meiner Anwendung ist es auch ziemlich langsam. Am Ende musste ich die maximale Ausführungszeit in php.ini verlängern
– Chris Baker
12. Mai 2011 um 20:10 Uhr
Excel hat ein sehr “knappes” internes Binärformat, und PHPExcel muss durch viele Hürden springen, um PHP-Datentypen in die internen Darstellungen von Excel zu konvertieren. Es gibt nicht viel, was Sie tun können, um die Dinge zu beschleunigen, außer die Datenmenge zu reduzieren, die in die Tabelle gelangt.
– Markus B
12. Mai 2011 um 20:27 Uhr
@Marc, außer dass dies das Excel 2010-Format ist, das XML ist. Meine Vermutung, basierend auf dem Speicherbegrenzungsfehler, ist, dass PHPExcel DOM (oder noch schlimmer einen alten PHP-basierten XML-Parser) verwendet, um die XML-Datei in den Speicher zu laden. So etwas sollte wahrscheinlich XMLReader/XMLWriter verwenden und die Ergebnisse vergessen, nachdem die Operationen abgeschlossen sind (außer vielleicht Indizierungsbereiche in einer Datei).
– Kevin Peno
12. Mai 2011 um 20:42 Uhr
@Kevin: Stimmt, aber selbst innerhalb des XML-Formats gibt es immer noch große Mengen an cdata-Abschnitten mit binären Blobs im alten Stil, die aus den alten Formaten übernommen wurden.
– Markus B
12. Mai 2011 um 20:43 Uhr
Am Ende fügte ich set_time_limit(30) bei jeder Iteration einiger der Schleifen hinzu, um die maximale Ausführungszeit zu verlängern.
– Salzige Nüsse
17. Mai 2011 um 14:37 Uhr
Markus Bäcker
Füllt es das Arbeitsblatt? oder sparen? dass Sie zu langsam finden?
Wie füllen Sie die Tabelle mit den Daten?
Verwendung der fromArray() Methode ist effizienter als das Ausfüllen jeder einzelnen Zelle, insbesondere wenn Sie den erweiterten Wertbinder verwenden, um Zelldatentypen automatisch festzulegen.
Wenn Sie Werte für jede einzelne Zelle in einem Blatt mit festlegen
so dass Sie nur auf die zugreifen getActiveSheet() Methode einmal; oder nutzen Sie die fließende Schnittstelle, um mehrere Zellen mit nur einem einzigen Anruf einzurichten $objPHPExcel->getActiveSheet()
Sie haben das Anwenden von Stilen auf Zellbereiche kommentiert:
Sie haben auch die Möglichkeit zu verwenden applyFromArray() um eine ganze Reihe von Style-Einstellungen auf einmal vorzunehmen.
Es ist viel effizienter, wenn Sie Stile auf eine Spalte oder eine Zeile anwenden können, anstatt nur auf einen Bereich
Wenn Sie Formeln in Ihrer Arbeitsmappe verwenden, gilt beim Speichern Folgendes:
Verwenden
$objWriter->setPreCalculateFormulas(false)
um die Berechnung der Formeln in PHPExcel selbst zu deaktivieren.
Dies sind nur ein paar Tipps zur Steigerung der Leistung, und in den Forenthreads gibt es noch viele weitere Vorschläge. Sie werden nicht unbedingt alle helfen, zu viel hängt von Ihrer spezifischen Arbeitsmappe ab, um absolute Angaben zu machen, aber Sie sollten in der Lage sein, diese langsame Geschwindigkeit zu verbessern. Sogar das kleine Notebook, das ich für die Entwicklung verwende, kann eine Excel 2007-Datei mit 3 Arbeitsblättern, 20 Spalten und 2.000 Zeilen schneller schreiben als Ihr Produktionsserver.
BEARBEITEN
Wenn es möglich wäre, einfach die Geschwindigkeit von PHPExcel selbst zu verbessern, hätte ich das längst getan. So wie es ist, teste ich ständig die Leistung, um zu sehen, wie die Geschwindigkeit verbessert werden kann. Wenn Sie schnellere Geschwindigkeiten wünschen, als PHPExcel selbst bieten kann, finden Sie hier eine Liste alternativer Bibliotheken.
Ist das auch jemandem aufgefallen $sheet->insertNewRowBefore($row) allein (innerhalb einer Schleife) verursacht auch einige Leistungseinbußen?
– javiniar.leonard
21. Januar 2016 um 9:30 Uhr
@javiniar.leonard – insertNewRowBefore() ist immer teuer, und zwar mehr beim Einlegen am oberen Rand des Blattes als am unteren Ende: Versuchen Sie immer, es nicht in einer Schleife zu verwenden; Sie können eine Anzahl von einzufügenden Zeilen als zweites Argument angeben, und das ist viel effizienter, als jede Zeile einzeln einzufügen
– Markus Bäcker
21. Januar 2016 um 10:16 Uhr
Ebenfalls xdebug kann ein Leistungsengpass sein, ich habe gesehen, dass PHPExcel viel langsamer ist, wenn es aktiv ist.
– Tonix
18. Januar 2018 um 9:35 Uhr
xdebug verlangsamt die Codeausführung, unabhängig davon, welches PHP Sie ausführen, PHPExcel ist da keine Ausnahme
– Markus Bäcker
18. Januar 2018 um 9:39 Uhr
Supergeschwindigkeit: Das Verschieben von $sheet->insertNewRowBefore($row) außerhalb der Schleife hat die Geschwindigkeit drastisch erhöht. Es ist sehr einfach, die Länge der Zeilen zu kennen und das Vorbefüllen vor der Schleife hat meine Datei mit 250 Zeilen und 22 Spalten von 6 Sekunden auf Millisekunden erhöht.
– Abdel Rehman
18. Dezember 2018 um 9:34 Uhr
Katrina
Ich bin auch auf dieses Problem gestoßen. Ich dachte, ich würde meinen Senf dazu geben, da diese Frage so viele Aufrufe erhält.
Zellenwerte einstellen
Anstatt den Wert für jede Zelle einzeln festzulegen, verwenden Sie die fromArray() Methode. Entnommen und modifiziert aus das 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)
);
Zellen stylen
Statisch
Es ist auch schneller, die Stile für einen Bereich anzuwenden, als den Stil für jede Zelle einzeln festzulegen (ein Muster bemerken??).
Ich verwende PHPExcel, um die in der Tabelle angegebenen Daten mit den aktuellen Daten in der Datenbank zu vergleichen. Da jede Zelle einzeln überprüft wird, habe ich die Stile in ein Array eingefügt (null für keinen Stil) und die Schleife unten verwendet, um den Zellbereich zu erhalten, auf den der Stil angewendet werden soll.
/*
* $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;
}
}
fromArray ist unglaublich schneller!
–Andreas
31. Mai 2016 um 21:18 Uhr
Ich hatte das gleiche Problem – hatte ungefähr 450 Zeilen mit 11 Datenspalten, die ich zu schreiben versuchte, und ich stieß immer wieder auf das 30-Sekunden-Timeout. Ich konnte die Ausführungszeit auf 2 Sekunden oder weniger reduzieren, indem ich alle meine neuen Zeilen in großen Mengen hinzufügte und dann den Zelleninhalt durchging und nachträglich festlegte. Mit anderen Worten, ich füge 450 Zeilen in einem Aufruf von insertNewRowBefore() ein und führe dann eine Schleife durch und lege später Inhalt in diesen Zeilen fest.
So:
$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++;
}
Ich füge Zeilen eigentlich nie explizit hinzu, außer ein paar Kopfzeilen, die ich hinzufüge, nachdem alle Daten eingegeben wurden, nur um die Berechnung der Datenzellenkoordinaten zu vereinfachen.
– Salzige Nüsse
5. April 2012 um 15:24 Uhr
Ich habe deinen Vorschlag mit ausprobiert ->insertNewRowBefore(). Ich schreibe ungefähr 6000 Zeilen in zwei Blättern. Der Kreislauf ->setCellValue ohne zu benutzen ->insertNewRowBefore nahm 151 Sekunden und mit verwenden ->insertNewRowBeforees dauerte 147 Sekunde. Daher kann ich deine Lösung leider nicht bestätigen 🙁
– Summe
28. November 2013 um 20:14 Uhr
Ich bin keineswegs ein Experte in der Verwendung von PHPExcel, aber Das OfficeOpenXML-Format (das Format von *.xlsx-Dateien) ist selbst eine Gruppe von XML-Dateien, die in einem ZIP-Archiv mit der Erweiterung *.xlsx gepackt sind. Wenn Sie Ihre Leistung schätzen und wissen, welche Art von Daten Sie übergeben werden, ist es vielleicht eine bessere Idee, dies zu tun Eigenen XLSX-Generator bauenreduziert auf die wichtigsten Funktionen, vielleicht einige Berechnungen auf Datenbankebene usw., anstatt das gesamte Dokument zu parsen.
Dazu können Sie mit der Analyse von Dateien beginnen, die mit kleineren Datensätzen erstellt wurden (indem Sie die Erweiterung von *.xlsx in *.zip ändern, entpacken und den Inhalt der einzelnen Dateien durchsuchen). Auf diese Weise können Sie feststellen, was Sie wirklich brauchen, und es selbst generieren (indem Sie geeignete XML-Dateien erstellen und in ein ZIP-Archiv packen und dann in die Erweiterung *.xlsx umbenennen).
Es gibt auch eine Spezifikation von OfficeOpenXML, das umfangreich ist (ein paar tausend Seiten), daher schlage ich nicht vor, es zu lesen, es sei denn, Sie möchten es wirklich. Es sollte ausreichen, Dateien so zu erstellen, wie sie von PHPExcel generiert wurden.
Die oben erwähnte Lösung enthält keine PHPExcel-bezogenen Tipps, da ich kein Experte darin bin. Ich habe mich jedoch schon früher für den OOXML-Standardisierungsprozess interessiert und würde mich freuen, wenn Ihnen die Kenntnis dieses Standards bei der Lösung Ihres Problems helfen würde.
Bei einem XLSX-Export mit Spalten a – amj (~800) und nur ~50 Zeilen bin ich auch an die 30-Sekunden-Grenze gelaufen. Um mein Programm zu testen, habe ich die Anzahl der verarbeiteten Zeilen auf nur 7 begrenzt, was in 25 Sekunden funktionierte.
Beim Wechseln von einzelnen $objPHPExcel->getActiveSheet() zu $sheet (erster Ratschlag) wurde die Zeit für eine begrenzte Anzahl von Zeilen tatsächlich von 25 Sekunden auf 26 Sekunden erhöht.
Was mir wirklich geholfen hat, war das Ersetzen aller meiner getHighestDataColumn() durch eine einfache $column_nr-Variable, die in PHP erhöht wird, ich ging von 26 Sekunden auf 7 Sekunden.
Danach konnte ich alle 50 Zeilen in 11 Sekunden verarbeiten.
eine Pflaume
Ein Leistungstipp, den ich zuvor noch nicht gesehen hatte, bezieht sich auf das Hinzufügen von Arbeitsblättern – oder genauer gesagt auf das Festlegen des Titels eines Arbeitsblatts. Wenn Sie viele Arbeitsblätter hinzufügen, kann die Reihenfolge der Vorgänge große Auswirkungen haben. Für die folgenden Tests habe ich eine Tabelle mit 120 ausgefüllten Arbeitsblättern verwendet und die Zeit gemessen, die es dauerte, weitere 120 leere Arbeitsblätter zu erstellen.
Verwenden Sie zunächst die in gezeigten Schritte die Dokumente:
Der größte Teil der Leistungslücke zwischen den beiden oben genannten Methoden kann durch Verwendung des zweiten Parameters von geschlossen werden setTitle (wenn dies in Ihrem Fall sicher ist; siehe die Dokumente):
Ich hatte genau das gleiche Problem. Ich habe eine CSV-Datei mit 5000 Zeilen und 32 Spalten erhalten, deren Verarbeitung ewig gedauert hat. Es stellt sich heraus, dass fast die gesamte Zeit, die für die “Verarbeitung” aufgewendet wird, tatsächlich die Zeichencodierung ist, die standardmäßig so eingestellt ist, dass alles in UTF8 codiert wird. Wenn Sie also in Ihre Datei config\excel.php gehen und nach unten zur Codierung scrollen, stellen Sie sie einfach wie folgt ein:
Allein damit dauert die Verarbeitung der oben genannten Datei etwa 8 Sekunden. Möglicherweise möchten Sie Ihren Client jedoch warnen, die CSV-Datei korrekt zu speichern.
13120100cookie-checkPHPExcel sehr langsam – Möglichkeiten zur Verbesserung?yes
Ich verwende diese Bibliothek für einen Bericht, der nur 40 Datenzeilen enthält, aber SEHR stilisiert ist und 2 Seiten breit ist, sodass ich viele Spalten habe. In meiner Anwendung ist es auch ziemlich langsam. Am Ende musste ich die maximale Ausführungszeit in php.ini verlängern
– Chris Baker
12. Mai 2011 um 20:10 Uhr
Excel hat ein sehr “knappes” internes Binärformat, und PHPExcel muss durch viele Hürden springen, um PHP-Datentypen in die internen Darstellungen von Excel zu konvertieren. Es gibt nicht viel, was Sie tun können, um die Dinge zu beschleunigen, außer die Datenmenge zu reduzieren, die in die Tabelle gelangt.
– Markus B
12. Mai 2011 um 20:27 Uhr
@Marc, außer dass dies das Excel 2010-Format ist, das XML ist. Meine Vermutung, basierend auf dem Speicherbegrenzungsfehler, ist, dass PHPExcel DOM (oder noch schlimmer einen alten PHP-basierten XML-Parser) verwendet, um die XML-Datei in den Speicher zu laden. So etwas sollte wahrscheinlich XMLReader/XMLWriter verwenden und die Ergebnisse vergessen, nachdem die Operationen abgeschlossen sind (außer vielleicht Indizierungsbereiche in einer Datei).
– Kevin Peno
12. Mai 2011 um 20:42 Uhr
@Kevin: Stimmt, aber selbst innerhalb des XML-Formats gibt es immer noch große Mengen an cdata-Abschnitten mit binären Blobs im alten Stil, die aus den alten Formaten übernommen wurden.
– Markus B
12. Mai 2011 um 20:43 Uhr
Am Ende fügte ich set_time_limit(30) bei jeder Iteration einiger der Schleifen hinzu, um die maximale Ausführungszeit zu verlängern.
– Salzige Nüsse
17. Mai 2011 um 14:37 Uhr