如何匯出千萬級別資料?

dj1540225203發表於2020-11-25

簡單參考:PHP快速匯出百萬級資料到CSV或者EXCEL檔案

複雜參考:PHP百萬級資料匯出方案(多csv檔案壓縮)

//簡單的使用
function test(){
 set_time_limit(0);
        //設定程式執行記憶體
        ini_set('memory_limit', '128M');

        $fileName = '測試匯出資料';
        header('Content-Encoding: UTF-8');
        header("Content-type:application/vnd.ms-excel;charset=UTF-8");
        header('Content-Disposition: attachment;filename="' . $fileName . '.csv"');

        //開啟php標準輸出流
        $fp = fopen('php://output', 'a');

        //新增BOM頭,以UTF8編碼匯出CSV檔案,如果檔案頭未新增BOM頭,開啟會出現亂碼。
        fwrite($fp, chr(0xEF) . chr(0xBB) . chr(0xBF));
        //新增匯出標題
        fputcsv($fp, ['姓名', '年齡', '地區']);

        //連結資料庫
        $dsn = "mysql:host=XXX;port=XX;dbname=XX;charset=utf8";
        $pdo = new PDO($dsn, 'XX', 'XX');

        $step = 100; //迴圈次數
        $nums = 10000; //每次匯出數量

        for ($i = 0; $i < $step; $i++) {
            $start = $i * 10000;
            $sql = "SELECT username,province,aa FROM `user` ORDER BY `id` LIMIT {$start},{$nums}";
            $pdostatement = $pdo->query($sql);
            $result = $pdostatement->fetchAll(PDO::FETCH_ASSOC);
            foreach ($result as $item) {
                fputcsv($fp, $item);
            }
            //每1萬條資料就重新整理緩衝區
            ob_flush();
            flush();
        }
        exit();
}
//複雜的使用

 //匯出說明:因為EXCEL單表只能顯示104W資料,同時使用PHPEXCEL容易因為資料量太大而導致佔用記憶體過大,
    //因此,資料的輸出用csv檔案的格式輸出,但是csv檔案用EXCEL軟體讀取同樣會存在只能顯示104W的情況,所以將資料分割儲存在多個csv檔案中,並且最後壓縮成zip檔案提供下載
    function putCsv(array $head, $data, $mark = 'attack_ip_info', $fileName = "test.csv")
    {
        set_time_limit(0);
        $sqlCount = $data->count();
        // 輸出Excel檔案頭,可把user.csv換成你要的檔名
        header('Content-Type: application/vnd.ms-excel;charset=utf-8');
        header('Content-Disposition: attachment;filename="' . $fileName . '"');
        header('Cache-Control: max-age=0');

        $sqlLimit = 100000;//每次只從資料庫取100000條以防變數快取太大
        // 每隔$limit行,重新整理一下輸出buffer,不要太大,也不要太小
        $limit = 100000;
        // buffer計數器
        $cnt = 0;
        $fileNameArr = array();
        // 逐行取出資料,不浪費記憶體
        for ($i = 0; $i < ceil($sqlCount / $sqlLimit); $i++) {
            $fp = fopen($mark . '_' . $i . '.csv', 'w'); //生成臨時檔案
      //     chmod('attack_ip_info_' . $i . '.csv',777);//修改可執行許可權
            $fileNameArr[] = $mark . '_' .  $i . '.csv';
        // 將資料通過fputcsv寫到檔案控制程式碼
            fputcsv($fp, $head);
            $dataArr = $data->offset($i * $sqlLimit)->limit($sqlLimit)->get()->toArray();
            foreach ($dataArr as $a) {
                $cnt++;
                if ($limit == $cnt) {
                    //重新整理一下輸出buffer,防止由於資料過多造成問題
                    ob_flush();
                    flush();
                    $cnt = 0;
                }
                fputcsv($fp, $a);
            }
            fclose($fp);  //每生成一個檔案關閉
        }
        //進行多個檔案壓縮
        $zip = new ZipArchive();
        $filename = $mark . ".zip";
        $zip->open($filename, ZipArchive::CREATE);   //開啟壓縮包
        foreach ($fileNameArr as $file) {
            $zip->addFile($file, basename($file));   //向壓縮包中新增檔案
        }
        $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); //刪除壓縮包臨時檔案
    }

 

相關文章