PHP generiert und lädt Excel-Dateien mit extrem großen Datenmengen in Echtzeit herunter

藏色散人
Freigeben: 2023-04-07 10:42:02
nach vorne
3962 Leute haben es durchsucht

Kürzlich erhielt ich eine Anfrage, die entsprechenden Benutzerzugriffsprotokolle für den ausgewählten Zeitraum zu exportieren. Aufgrund der großen Anzahl von Benutzern werden häufig mehr als 500.000 Daten exportiert.

Das häufig verwendete PHPexcel-Paket muss alle Daten abrufen, bevor es Excel generieren kann. Dies führt offensichtlich zu einem Speicherüberlauf, wenn eine Excel-Datei mit einer großen Datenmenge generiert wird. Erwägen Sie daher die Verwendung von PHP zum Schreiben Gleichzeitig ermöglicht der Ausgabestream dem Browser, die Anfrage in Form eines Downloads abzuschließen.

Wir schreiben den PHP-Ausgabestream wie folgt:

$fp = fopen('php://output', 'a');
fputs($fp, 'strings');
....
....
fclose($fp)
Nach dem Login kopieren

php://output ist ein beschreibbarer Ausgabestream, der es dem Programm ermöglicht, Ausgaben wie eine Datei in den Ausgabestream zu schreiben sendet den Inhalt im Ausgabestream an den Webserver und gibt ihn an den Browser zurück, der die Anfrage initiiert hat

Da die Excel-Daten außerdem nach und nach aus der Datenbank gelesen und dann in den Ausgabestream geschrieben werden, ist die Die Ausführung von PHP muss länger sein. Stellen Sie die Zeit länger ein (Standard 30 Sekunden). set_time_limit(0) begrenzt die PHP-Ausführungszeit nicht.

Hinweis:

Der folgende Code veranschaulicht nur die Ideen und Schritte zum Generieren von Excel mit großen Datenmengen. Nach dem Entfernen des Projektgeschäftscodes weist das Programm Syntaxfehler auf und nicht direkt ausgeführt werden kann, füllen Sie bitte den entsprechenden Geschäftscode entsprechend Ihren eigenen Bedürfnissen aus!

/**
     * 文章访问日志
     * 下载的日志文件通常很大, 所以先设置csv相关的Header头, 然后打开
     * PHP output流, 渐进式的往output流中写入数据, 写到一定量后将系统缓冲冲刷到响应中
     * 避免缓冲溢出
     */
    public function articleAccessLog($timeStart, $timeEnd)
    {
        set_time_limit(0);
        $columns = [
            '文章ID', '文章标题', ......
        ];
        $csvFileName = '用户日志' . $timeStart .'_'. $timeEnd . '.xlsx';
        //设置好告诉浏览器要下载excel文件的headers
        header('Content-Description: File Transfer');
        header('Content-Type: application/vnd.ms-excel');
        header('Content-Disposition: attachment; filename="'. $fileName .'"');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        $fp = fopen('php://output', 'a');//打开output流
        mb_convert_variables('GBK', 'UTF-8', $columns);
        fputcsv($fp, $columns);//将数据格式化为CSV格式并写入到output流中
        $accessNum = '1000000'//从数据库获取总量,假设是一百万
        $perSize = 1000;//每次查询的条数
        $pages   = ceil($accessNum / $perSize);
        $lastId  = 0;
        for($i = 1; $i <= $pages; $i++) {
            $accessLog = $logService->getArticleAccessLog($timeStart, $timeEnd, $lastId, $perSize);
            foreach($accessLog as $access) {
                $rowData = [
                    ......//每一行的数据
                ];
                mb_convert_variables(&#39;GBK&#39;, &#39;UTF-8&#39;, $rowData);
                fputcsv($fp, $rowData);
                $lastId = $access->id;
            }
            unset($accessLog);//释放变量的内存
            //刷新输出缓冲到浏览器
            ob_flush();
            flush();//必须同时使用 ob_flush() 和flush() 函数来刷新输出缓冲。
        }
        fclose($fp);
        exit();
    }
Nach dem Login kopieren

Okay, es ist eigentlich ganz einfach, den Ausgabestream Schritt für Schritt zu schreiben und ihn an den Browser zu senden, damit der Browser die gesamte Datei Schritt für Schritt herunterlädt. Die Gesamtgröße der Datei kann nicht ermittelt werden, daher gibt es keine Die Methode besteht darin, dem Browser vor dem Herunterladen mitzuteilen, wie groß die Datei ist, indem header("Content-Length: $size"); festgelegt wird. Dies hat jedoch keinen Einfluss auf den Gesamteffekt. Das Kernproblem besteht darin, die Echtzeitgenerierung und den Download großer Dateien zu lösen.

Update: Lassen Sie mich hier über meine Idee der Datenbankabfrage sprechen, da die nach und nach in Excel geschriebenen Daten tatsächlich aus der Paging-Abfrage von MySQL stammen. Jeder weiß, dass die Syntax LIMIT ist Offset, Num, aber je größer der Offset, desto mehr Zeilen muss MySQL in jeder Paging-Abfrage überspringen, was die Effizienz von MySQL-Abfragen (einschließlich NoSQL wie MongoDB) erheblich beeinträchtigt. Es wird nicht empfohlen, mehrere Zeilen zu überspringen, um die zu erhalten Ergebnissatz), also verwende ich LastId, um Paging-Abfragen durchzuführen.

Ähnlich der folgenden Aussage:

SELECT columns FROM `table_name` 
WHERE `created_at` >= &#39;time range start&#39; 
AND `created_at` <= &#39;time range end&#39; 
AND  `id` < LastId 
ORDER BY `id` DESC 
LIMIT num
Nach dem Login kopieren

Das obige ist der detaillierte Inhalt vonPHP generiert und lädt Excel-Dateien mit extrem großen Datenmengen in Echtzeit herunter. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:segmentfault.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage