php thinkphp5.1 導出百萬數據方案


先說一下一般我們在執行大數據操作時,遇到最常見的問題就是:
1、執行超時 參考這里
2、內存溢出 參考這里

看了這兩個之后再來看這個方案,實現思路大概是:

  • 先利用set_time_limit(0)不限制執行時間
  • 將要查詢的數據分段查詢,每次查詢創建一個csv臨時文件,每次清空緩沖區
  • 數據全部寫完之后將所有csv文件合並成一個csv文件
  • 合並之后利用zip擴展進行打包下載(如果沒有安裝此擴展,請先去安裝擴展)
  • 最后將所有生成的文件刪除

安裝擴展,PHP開啟自帶的ZipArchive類,實現壓縮解壓功能

Windows環境:

  1. 首先需要從官網上下載,下載地址 https://windows.php.net/downloads/pecl/releases/zip/

  2. 打開官網列表后需要查找適合自己的PHP版本和系統的zip,我的PHP版本是5.5的,這里我選擇的版本號是1.13.5


     
  3. 下載完后解壓,把里面的php_zip.dll文件放到PHP的擴展文件夾里


     

擴展文件夾路徑一般都是在PHP版本文件夾里的ext文件夾,譬如我的就是D:\php-5.5.38\ext

  1. 把php_zip.dll文件放進去后,打開PHP的配置文件php.ini,添加extension=php_zip.dll,保存后,重啟apache服務器

Linux環境:
1、在Linux下沒有php_zip.dll這個文件(有也不會起作用的),所以需要重新編譯一下php的zip模塊。具體安裝方法如下:

cd /usr/src

wget http://pecl.php.net/get/zip

tar -zxvf zip

cd zip-1.x.x

phpize

./configure

make

sudo make instal

其中, 在最后使用make install命令的時候,可能需要用到root的權限,所以建議使用sudo來運行。安裝完之后,屏幕上會提示zip.so的位置。然后將其記錄下來,如:/usr/local/lib/php/extensions/zip.so。

2、使用root權限修改php.ini(通常可能會在/usr/local/lib/文件夾下,不過視當初安裝php而定,可以通過phpinfo()來查看):

增加extension = /usr/local/lib/php/extensions/zip.so,然后同樣在php.ini文件中,將 zlib.output_compression = Off 改為 zlib.output_compression = On ;

3、最后別忘了重啟一下Apache:apachectl restart;

這個針對php的zip模塊就安裝完成了,能夠在php中使用ZipArchive類了。

    /**
     * 百萬級數據導出
     */
    public function excelout(){
        //不限制執行時間,以防超時
        set_time_limit(0);
        //文件名
        $xlsName = '名字'.date('Ymd His');
        //統計總行數
        $sqlCount = 0;

        //表頭
        $xlsCell = ['姓名','性別','年齡','電話','QQ','地址'];
        //對應表頭的字段
        $fields = 'name,sex,age,tel,qq,address';

        //統計總行數
        $sqlCount = UserModel::count();

        //每次取多少條
        $sqlLimit = 2000;//每次只從數據庫取2000條

        // buffer計數器
        $cnt = 0;

        $fileNameArr = array();

        //分段執行,以免內存寫滿
        for ($i = 0; $i < ceil($sqlCount / $sqlLimit); $i++) {
            $fp = fopen($xlsName . '_' . $i . '.csv', 'w'); //生成臨時文件

            $fileNameArr[] = $xlsName . '_' .  $i . '.csv';//將臨時文件保存起來

            //第一次執行時將表頭寫入
            if($i == 0){
                fputcsv($fp, $xlsCell);
            }

            //查詢出數據
            $xlsData = UserModel::field($fields)
                ->limit($i * $sqlLimit,$sqlLimit)
                ->select()->toArray();

            foreach ($xlsData as $k=>$v) {
                $cnt++;

                //執行下一次循環之前清空緩沖區
                if ($sqlLimit == $cnt) {
                    ob_flush();
                    $cnt = 0;
                }

                //每行寫入到臨時文件
                fputcsv($fp, $v);
            }
            fclose($fp);  //每生成一個文件關閉
        }

        //將所有臨時文件合並成一個
        foreach ($fileNameArr as $file){
            //如果是文件,提出文件內容,寫入目標文件
            if(is_file($file)){
                $fileName = $file;
                //打開臨時文件
                $handle1 = fopen($fileName,'r');
                //讀取臨時文件
                if($str = fread($handle1,filesize($fileName))){
                    //關閉臨時文件
                    fclose($handle1);
                    //打開或創建要合並成的文件,往末尾插入的方式添加內容並保存
                    $handle2 = fopen($xlsName.'.csv','a+');
                    //寫入內容
                    if(fwrite($handle2, $str)){
                        //關閉合並的文件,避免浪費資源
                        fclose($handle2);
                    }
                }
            }
        }

        //將文件壓縮,避免文件太大,下載慢
        $zip = new \ZipArchive();
        $filename = $xlsName . ".zip";
        $zip->open($filename, \ZipArchive::CREATE);   //打開壓縮包
        $zip->addFile($xlsName.'.csv', basename($xlsName.'.csv'));   //向壓縮包中添加文件
        $zip->close();  //關閉壓縮包

        foreach ($fileNameArr as $file) {
            unlink($file); //刪除csv臨時文件
        }

        //輸出壓縮文件提供下載
        header("Cache-Control: max-age=0");
        header("Content-Description: File Transfer");
        header('Content-disposition: attachment; filename=' . basename($filename)); // 文件名
        header("Content-Type: application/zip"); // zip格式的
        header("Content-Transfer-Encoding: binary"); //
        header('Content-Length: ' . filesize($filename)); //
        @readfile($filename);//輸出文件;
        unlink($filename); //刪除壓縮包臨時文件
        unlink($xlsName.'.csv'); //刪除合並的臨時文件
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM