webentwicklung-frage-antwort-db.com.de

Lesen sehr großer Dateien in PHP

fopen schlägt fehl, wenn ich versuche, eine sehr kleine Datei in PHP einzulesen. A 6 meg file verschluckt es, obwohl kleinere Dateien um 100k in Ordnung sind. Ich habe gelesen, dass es manchmal notwendig ist, PHP mit dem -D_FILE_OFFSET_BITS=64-Flag neu zu kompilieren, um Dateien mit mehr als 20 Gigs oder etwas Lächerliches zu lesen. Irgendwann wollen wir Dateien einlesen, die etwa 100 Megabyte groß sind, und es wäre schön, sie zu öffnen und sie dann zeilenweise mit Fits durchzulesen, wie ich es bei kleineren Dateien tun kann.

Was sind Ihre Tricks/Lösungen für das Lesen und Ausführen sehr großer Dateien in PHP?

Update: Hier ist ein Beispiel eines einfachen Codeblocks, der bei meiner 6-Megadatei fehlschlägt - PHP scheint keinen Fehler zu melden, es gibt nur false zurück. Vielleicht mache ich etwas extrem Dummes?

$rawfile = "mediumfile.csv";

if($file = fopen($rawfile, "r")){  
  fclose($file);
} else {
  echo "fail!";
}

Noch ein Update: Vielen Dank für Ihre Hilfe, es stellte sich heraus, dass es etwas unglaublich Dummes war - eine Berechtigungsfrage. Meine kleine Datei hatte unerklärlicherweise Leseberechtigungen, wenn die größere Datei dies nicht tat. Doh!

24
user5564

Sind Sie sicher, dass fopen fehlschlägt und nicht die Timeout-Einstellung Ihres Skripts? Der Standardwert liegt normalerweise bei etwa 30 Sekunden. Wenn Ihre Datei länger als das Einlesen benötigt, wird dies möglicherweise ausgelöst.

Eine andere Sache, die Sie berücksichtigen sollten, ist die Speicherbeschränkung in Ihrem Skript. Wenn Sie die Datei in ein Array schreiben, wird dies möglicherweise ausgelöst. Überprüfen Sie daher das Fehlerprotokoll auf Speicherwarnungen.

Wenn keines der oben genannten Probleme Ihr Problem ist, können Sie die Datei mit fgets verwenden, um die Datei Zeile für Zeile zu lesen und die Verarbeitung fortzusetzen.

$handle = fopen("/tmp/uploadfile.txt", "r") or die("Couldn't get handle");
if ($handle) {
    while (!feof($handle)) {
        $buffer = fgets($handle, 4096);
        // Process buffer here..
    }
    fclose($handle);
}

Bearbeiten

PHP scheint keine Fehlermeldung zu geben, es gibt nur false zurück.

Ist der Pfad zu $rawfile relativ zu dem Ort, an dem das Skript ausgeführt wird? Versuchen Sie vielleicht, einen absoluten Pfad für den Dateinamen festzulegen.

43
ConroyP

Hat 2 Tests mit einer 1,3 GB-Datei und einer 9,5 GB-Datei durchgeführt.

1,3 GB

Verwenden von fopen()

Dieser Prozess verwendete 15555 ms für seine Berechnungen.

Es verbrachte 169 ms bei Systemaufrufen.

Verwenden von file()

Dieser Prozess verwendete 6983 ms für seine Berechnungen.

In Systemaufrufen wurden 4469 ms ausgegeben.

9,5 GB

Verwenden von fopen()

Dieser Prozess verwendete 113559 ms für seine Berechnungen.

Es verbrachte 2532 ms in Systemaufrufen.

Verwenden von file()

Dieser Prozess verwendete 8221 ms für seine Berechnungen.

In Systemaufrufen wurden 7998 ms ausgegeben.

Scheint file() ist schneller.

7
Al-Punk

• Die Funktion fgets() ist in Ordnung, bis die Textdateien 20 MByte überschritten haben und die Analysegeschwindigkeit stark reduziert ist.

• Die Funktion file_ get_contents() liefert gute Ergebnisse bis 40 MByte und akzeptable Ergebnisse bis 100 MByte, aber file_get_contents() lädt die gesamte Datei in den Speicher . Es ist also nicht skalierbar.

• Die Funktion file() ist bei großen Textdateien katastrophal, da diese Funktion ein Array erstellt, das jede Textzeile enthält. Daher wird dieses Array im Speicher gespeichert und der verwendete Speicher ist noch größer.
Eine 200-MB-Datei, die ich nur mit memory_limit (2 GB) analysieren konnte, war für die über 1 GB großen Dateien, die ich analysieren wollte, ungeeignet.

Wenn Sie Dateien analysieren müssen, die größer als 1 GB sind, und die Analysezeit 15 Sekunden überschreitet und Sie vermeiden möchten, die gesamte Datei in den Speicher zu laden, müssen Sie einen anderen Weg finden.

Meine Lösung bestand darin, Daten in willkürlichen kleinen Blöcken zu analysieren. Der Code lautet:

$filesize = get_file_size($file);
$fp = @fopen($file, "r");
$chunk_size = (1<<24); // 16MB arbitrary
$position = 0;

// if handle $fp to file was created, go ahead
if ($fp) {
   while(!feof($fp)){
      // move pointer to $position in file
      fseek($fp, $position);

      // take a slice of $chunk_size bytes
      $chunk = fread($fp,$chunk_size);

      // searching the end of last full text line
      $last_lf_pos = strrpos($chunk, "\n");

      // $buffer will contain full lines of text
      // starting from $position to $last_lf_pos
      $buffer = mb_substr($chunk,0,$last_lf_pos);

      ////////////////////////////////////////////////////
      //// ... DO SOMETHING WITH THIS BUFFER HERE ... ////
      ////////////////////////////////////////////////////

      // Move $position
      $position += $last_lf_pos;

      // if remaining is less than $chunk_size, make $chunk_size equal remaining
      if(($position+$chunk_size) > $filesize) $chunk_size = $filesize-$position;
      $buffer = NULL;
   }
   fclose($fp);
}

Der verwendete Speicher ist nur der $chunk_size Und die Geschwindigkeit ist etwas geringer als die mit file_ get_contents() erhaltene. Ich denke, PHP Group sollte meinen Ansatz verwenden, um die Analysefunktionen zu optimieren.

*) Finde die get_file_size() function hier .

1
Tinel Barb

Ich habe fopen zum Öffnen von Videodateien zum Streaming verwendet, ein Php-Skript als Video-Streaming-Server verwendet, und ich hatte keine Probleme mit Dateien mit einer Größe von mehr als 50/60 MB.

1
Enrico Murru

für mich war fopen() mit Dateien über 1 MB sehr langsam, file() ist viel schneller.

Beim Versuch, die Zeilen 100 gleichzeitig zu lesen und Stapeleinfügungen zu erstellen, dauert fopen() 37 Sekunden gegenüber file() 4 Sekunden. Muss der string->array-Schritt in file() sein

Ich würde alle Dateibearbeitungsoptionen ausprobieren, um herauszufinden, welche in Ihrer Anwendung am besten funktionieren.

0
RightClick

Nun, Sie könnten versuchen, die readfile-Funktion zu verwenden, wenn Sie die Datei nur ausgeben möchten.

Wenn dies nicht der Fall ist, sollten Sie über das Design der Anwendung nachdenken. Warum möchten Sie so große Dateien auf Webanfragen öffnen?

0
Fionn

Wenn das Problem durch das Erreichen der Speicherbegrenzung verursacht wird, können Sie versuchen, einen höheren Wert festzulegen (dies kann funktionieren oder nicht, abhängig von der Konfiguration von PHP).

dadurch wird die Speichergrenze auf 12 MB festgelegt

ini\_set("memory_limit","12M");
0