PHP實(shí)現(xiàn)實(shí)時(shí)生成并下載超大數(shù)據(jù)量的EXCEL文件詳解
前言
最近在工作中接到一個(gè)需求,通過選擇的時(shí)間段導(dǎo)出對(duì)應(yīng)的用戶訪問日志到excel中, 由于用戶量較大,經(jīng)常會(huì)有導(dǎo)出50萬(wàn)加數(shù)據(jù)的情況。而常用的PHPexcel包需要把所有數(shù)據(jù)拿到后才能生成excel, 在面對(duì)生成超大數(shù)據(jù)量的excel文件時(shí)這顯然是會(huì)造成內(nèi)存溢出的,所以考慮使用讓PHP邊寫入輸出流邊讓瀏覽器下載的形式來完成需求。
我們通過如下的方式寫入PHP輸出流
$fp = fopen('php://output', 'a'); fputs($fp, 'strings'); .... .... fclose($fp)
php://output
是一個(gè)可寫的輸出流,允許程序像操作文件一樣將輸出寫入到輸出流中,PHP會(huì)把輸出流中的內(nèi)容發(fā)送給web服務(wù)器并返回給發(fā)起請(qǐng)求的瀏覽器
另外由于excel數(shù)據(jù)是從數(shù)據(jù)庫(kù)里逐步讀出然后寫入輸出流的所以需要將PHP的執(zhí)行時(shí)間設(shè)長(zhǎng)一點(diǎn)(默認(rèn)30秒)set_time_limit(0)
不對(duì)PHP執(zhí)行時(shí)間做限制。
注:以下代碼只是闡明生成大數(shù)據(jù)量EXCEL的思路和步驟,并且在去掉項(xiàng)目業(yè)務(wù)代碼后程序有語(yǔ)法錯(cuò)誤不能拿來直接運(yùn)行,請(qǐng)根據(jù)自己的需求填充對(duì)應(yīng)的業(yè)務(wù)代碼!
/** * 文章訪問日志 * 下載的日志文件通常很大, 所以先設(shè)置csv相關(guān)的Header頭, 然后打開 * PHP output流, 漸進(jìn)式的往output流中寫入數(shù)據(jù), 寫到一定量后將系統(tǒng)緩沖沖刷到響應(yīng)中 * 避免緩沖溢出 */ public function articleAccessLog($timeStart, $timeEnd) { set_time_limit(0); $columns = [ '文章ID', '文章標(biāo)題', ...... ]; $csvFileName = '用戶日志' . $timeStart .'_'. $timeEnd . '.xlsx'; //設(shè)置好告訴瀏覽器要下載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);//將數(shù)據(jù)格式化為CSV格式并寫入到output流中 $accessNum = '1000000'//從數(shù)據(jù)庫(kù)獲取總量,假設(shè)是一百萬(wàn) $perSize = 1000;//每次查詢的條數(shù) $pages = ceil($accessNum / $perSize); $lastId = 0; for($i = 1; $i <= $pages; $i++) { $accessLog = $logService->getArticleAccessLog($timeStart, $timeEnd, $lastId, $perSize); foreach($accessLog as $access) { $rowData = [ ......//每一行的數(shù)據(jù) ]; mb_convert_variables('GBK', 'UTF-8', $rowData); fputcsv($fp, $rowData); $lastId = $access->id; } unset($accessLog);//釋放變量的內(nèi)存 //刷新輸出緩沖到瀏覽器 ob_flush(); flush();//必須同時(shí)使用 ob_flush() 和flush() 函數(shù)來刷新輸出緩沖。 } fclose($fp); exit(); }
好了, 其實(shí)很簡(jiǎn)單,就是用逐步寫入輸出流并發(fā)送到瀏覽器讓瀏覽器去逐步下載整個(gè)文件,由于是逐步寫入的無法獲取文件的總體size所以就沒辦法通過設(shè)置header("Content-Length: $size");
在下載前告訴瀏覽器這個(gè)文件有多大了。不過不影響整體的效果這里的核心問題是解決大文件的實(shí)時(shí)生成和下載。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- PHP導(dǎo)出MySQL數(shù)據(jù)到Excel文件(fputcsv)
- php+ajax實(shí)現(xiàn)帶進(jìn)度條的大數(shù)據(jù)排隊(duì)導(dǎo)出思路以及源碼
- phpadmin如何導(dǎo)入導(dǎo)出大數(shù)據(jù)文件及php.ini參數(shù)修改
- phpexcel導(dǎo)入excel處理大數(shù)據(jù)(實(shí)例講解)
- php 在線導(dǎo)入mysql大數(shù)據(jù)程序
- 淺析THINKPHP的addAll支持的最大數(shù)據(jù)量
- php+ajax導(dǎo)入大數(shù)據(jù)時(shí)產(chǎn)生的問題處理
- php 大數(shù)據(jù)量及海量數(shù)據(jù)處理算法總結(jié)
- php使用fputcsv實(shí)現(xiàn)大數(shù)據(jù)的導(dǎo)出操作詳解
相關(guān)文章
php實(shí)現(xiàn)字符串首字母轉(zhuǎn)換成大寫的方法
這篇文章主要介紹了php實(shí)現(xiàn)字符串首字母轉(zhuǎn)換成大寫的方法,涉及php中ucfirst及ucwords函數(shù)的使用技巧,需要的朋友可以參考下2015-03-03PHPStorm+Xdebug進(jìn)行emote Debug時(shí)無法進(jìn)入斷點(diǎn)問題排查
這篇文章主要介紹了PHPStorm+Xdebug進(jìn)行emote Debug時(shí)無法進(jìn)入斷點(diǎn)問題排查,遇到類似問題的同學(xué),可以詳細(xì)參考下2021-04-04flash用php連接數(shù)據(jù)庫(kù)的代碼
flash用php連接數(shù)據(jù)庫(kù)的代碼,需要的朋友可以參考下。2011-04-04php無需編譯安裝openssl擴(kuò)展的實(shí)現(xiàn)方法
下面小編就為大家分享一篇php無需編譯安裝openssl擴(kuò)展的實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-03-03php中magic_quotes_gpc對(duì)unserialize的影響分析
這篇文章主要介紹了php中magic_quotes_gpc對(duì)unserialize的影響,以實(shí)例的形式分析了magic_quotes_gpc安全過濾對(duì)unserialize造成的影響以及對(duì)此的解決方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-12-12php實(shí)現(xiàn)的Timer頁(yè)面運(yùn)行時(shí)間監(jiān)測(cè)類
這篇文章主要介紹了php實(shí)現(xiàn)的Timer頁(yè)面運(yùn)行時(shí)間監(jiān)測(cè)類,可實(shí)現(xiàn)按不同key檢測(cè)不同的運(yùn)行時(shí)間,需要的朋友可以參考下2014-09-09PHP+MySQL實(shí)現(xiàn)的簡(jiǎn)單投票系統(tǒng)實(shí)例
這篇文章主要介紹了PHP+MySQL實(shí)現(xiàn)的簡(jiǎn)單投票系統(tǒng),以完整實(shí)例形式分析了PHP+MySQL實(shí)現(xiàn)投票功能的具體功能代碼與相關(guān)技巧,需要的朋友可以參考下2016-02-02