Maison > Article > développement back-end > PHP génère et télécharge des fichiers EXCEL contenant des quantités extrêmement importantes de données en temps réel
Récemment reçu une demande d'exportation des journaux d'accès des utilisateurs correspondants pour exceler sur la période sélectionnée. En raison du grand nombre d'utilisateurs, il arrive souvent que plus de 500 000 données soient exportées.
Le package PHPexcel couramment utilisé doit obtenir toutes les données avant de pouvoir générer Excel. Cela entraînera évidemment un débordement de mémoire lorsque vous serez confronté à la génération d'un fichier Excel avec une grande quantité de données, pensez donc à utiliser PHP pour écrire. en même temps, le flux de sortie permet au navigateur de compléter la demande sous forme de téléchargement.
Nous écrivons le flux de sortie PHP de la manière suivante
$fp = fopen('php://output', 'a'); fputs($fp, 'strings'); .... .... fclose($fp)
php://output est un flux de sortie inscriptible qui permet au programme d'écrire la sortie dans le flux de sortie comme un fichier PHP. enverra le contenu du flux de sortie au serveur Web et le renverra au navigateur qui a initié la requête
De plus, puisque les données Excel sont progressivement lues depuis la base de données puis écrites dans le flux de sortie, le l'exécution de PHP doit être plus longue (30 secondes par défaut) set_time_limit(0) ne limite pas le temps d'exécution de PHP.
Remarque :
Le code suivant illustre uniquement les idées et les étapes pour générer EXCEL avec de grandes quantités de données. Après avoir supprimé le code métier du projet, le programme contient des erreurs de syntaxe. et ne peut pas être exécuté directement, veuillez remplir le code commercial correspondant selon vos propres besoins !
/** * 文章访问日志 * 下载的日志文件通常很大, 所以先设置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('GBK', 'UTF-8', $rowData); fputcsv($fp, $rowData); $lastId = $access->id; } unset($accessLog);//释放变量的内存 //刷新输出缓冲到浏览器 ob_flush(); flush();//必须同时使用 ob_flush() 和flush() 函数来刷新输出缓冲。 } fclose($fp); exit(); }
D'accord, c'est en fait très simple. Il s'agit d'écrire le flux de sortie étape par étape et de l'envoyer au navigateur pour laisser le navigateur télécharger l'intégralité du fichier étape par étape. la taille globale du fichier ne peut pas être obtenue, il n'y a donc pas de méthode. La méthode consiste à indiquer au navigateur la taille du fichier avant le téléchargement en définissant header("Content-Length: $size");. Cependant, cela n'affecte pas l'effet global. Le problème principal ici est de résoudre la génération et le téléchargement en temps réel de fichiers volumineux.
Mise à jour : Laissez-moi parler ici de mon idée de requête de base de données, car les données progressivement écrites dans EXCEL proviennent en fait de la requête de pagination de Mysql. Tout le monde sait que la syntaxe est LIMIT. offset, num, mais comme plus le décalage est grand, plus MySQL doit sauter de lignes dans chaque requête de pagination, ce qui affectera sérieusement l'efficacité des requêtes MySQL (y compris NoSQL tel que MongoDB, il n'est pas recommandé de sauter plusieurs lignes pour obtenir le jeu de résultats), j'utilise donc LastId pour effectuer des requêtes de pagination.
Semblable à la déclaration suivante :
SELECT columns FROM `table_name` WHERE `created_at` >= 'time range start' AND `created_at` <= 'time range end' AND `id` < LastId ORDER BY `id` DESC LIMIT num
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!