PHPExcel 大數據的導出


PHPExcel 是一個php語言讀取導出數據、導入生成Excel的類庫,使用起來非常方便,但有時會遇到以些問題,比如導出的數據超時,內存溢出等。

下面我們來說說這些問題和解決辦法。

PHPExcel 版本:@version    1.8.0, 2014-03-02

能遇到這樣的問題一般都是因為數據量大導致

1.PHPExcel 報錯

報錯提示:

'break' not in the 'loop' or 'switch' context

嚴格的講這個不是PHPExcel的錯誤,是PHP版本的問題,大於PHP5.6以后,“break”必須要在循環體內執行(for ,foreach, while, switch)

此處無循環,解決辦法:注釋掉break;

2.超時

提示:

Maximum execution time of 30 seconds exceeded

數據量過大,php執行超過30秒后就會報這樣的信息

解決辦法:

可修改php.ini  或直接在執行頁面中添加

set_time_limit(0);

這樣就設置了php的執行超時

 

3.內存溢出

超時解決好之后,等待了好幾十秒后又來了個錯誤:

Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes)

內存不足呀!

解決內存溢出我們分兩步走,

第一步:設置memory_limit

默認情況memory_limit 大小為100MB,當所需內存大於100MB就會溢出,所以設置足夠大的值

ini_set("memory_limit", "1024M");  // 根據電腦配置不夠繼續增加

第二步:設置PHPExcel單元格緩存

單元格緩存是將所需PHPExcel內存單元格對象緩存到磁盤、memcache、MemoryGZip等,這樣讀取上會更耗時,但可以降低內存的消耗。

PHPExcel_CachedObjectStorageFactory 這個類中提供了這幾個單元格緩存

    const cache_in_memory               = 'Memory';
    const cache_in_memory_gzip          = 'MemoryGZip';  #將單元格序列化后再進行Gzip壓縮,然后保存在內存中
    const cache_in_memory_serialized    = 'MemorySerialized';  # 將單元格數據序列化后保存在內存中
    const cache_igbinary                = 'Igbinary';    #存儲為緊密的二進制形式
    const cache_to_discISAM             = 'DiscISAM';    #緩存在臨時的磁盤文件中,速度可能會慢一些
    const cache_to_apc                  = 'APC';     #Alternative PHP Cache可選PHP緩存
    const cache_to_memcache             = 'Memcache';   #保存在memcache中
    const cache_to_phpTemp              = 'PHPTemp';    #保存在php://temp
    const cache_to_wincache             = 'Wincache';
    const cache_to_sqlite               = 'SQLite';
    const cache_to_sqlite3              = 'SQLite3';

每一個worksheet都會有一個獨立的緩存,當一個worksheet實例化時,就會根據設置或配置的緩存方式來自動創建。一旦你開始讀取一個文件或者你已經創建了第一個worksheet,就不能在改變緩存的方式了。

  • MemorySerialized: 使用這種緩存方式,單元格會以序列化的方式保存在內存中,這是降低內存使用率性能比較高的一種方案。 
  • MemoryGZip: 與序列化的方式類似,這種方法在序列化之后,又進行gzip壓縮之后再放入內存中,這回跟進一步降低內存的使用,但是讀取和寫入時會有一些慢。
  • DiscISAM:當使用cache_to_discISAM這種方式時,所有的單元格將會保存在一個臨時的磁盤文件中,只把他們的在文件中的位置保存在PHP的內存中,這會比任何一種緩存在內存中的方式都慢,但是能顯著的降低內存的使用。臨時磁盤文件在腳本運行結束是會自動刪除。
  • PHPTemp: 類 似cache_to_discISAM這種方式,使用cache_to_phpTemp時,所有的單元格會還存在php://temp I/O流中,只把 他們的位置保存在PHP的內存中。PHP的php://memory包裹器將數據保存在內存中,php://temp的行為類似,但是當存儲的數據大小超 過內存限制時,會將數據保存在臨時文件中,默認的大小是1MB,但是你可以在初始化時修改它。php://temp文件在腳本結束是會自動刪除。 

 

4.大數據導出

微軟的Excel設置單元格行數默認是6萬行rows,相對的講其實當我們超過1萬行的時候已經是大數據的導出。

好比:有客戶10000人,平均每人每天產生10條活動記錄,要導出上周所有的客戶活動記錄: 10000*10*7=700000

估計看70萬行的Excel這個人會瘋掉的,我們的建議是分批次導出,按時間導出到不同的excel

下面是一個PHPExcel官方的Demo(已修改過)

        define('EOL', '<br />');
        $objPHPExcel = new \app\extensions\PHPExcel\PHPExcel();
        ini_set("memory_limit", "1024M"); // 設置php可使用內存

        $cacheMethod = \PHPExcel_CachedObjectStorageFactory::cache_in_memory_gzip;
        if (!\PHPExcel_Settings::setCacheStorageMethod($cacheMethod)) {
            die($cacheMethod . " 緩存方法不可用" . EOL);
        }
        echo date('H:i:s'), " 當前使用的緩存方法是: ", $cacheMethod, " 方式", EOL;
        echo date('H:i:s'), " 開始設置文檔屬性", EOL;
        $objPHPExcel->getProperties()->setCreator("Maarten Balliauw")
                ->setLastModifiedBy("Maarten Balliauw")
                ->setTitle("Office 2007 XLSX Test Document")
                ->setSubject("Office 2007 XLSX Test Document")
                ->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.")
                ->setKeywords("office 2007 openxml php")
                ->setCategory("Test result file");


        echo date('H:i:s'), " 開始添加單元格標題", EOL;
        $objPHPExcel->setActiveSheetIndex(0);
        $objPHPExcel->getActiveSheet()->setCellValue('A1', "Firstname");
        $objPHPExcel->getActiveSheet()->setCellValue('B1', "Lastname");
        $objPHPExcel->getActiveSheet()->setCellValue('C1', "Phone");
        $objPHPExcel->getActiveSheet()->setCellValue('D1', "Fax");
        $objPHPExcel->getActiveSheet()->setCellValue('E1', "Is Client ?");
        
        // 設置單元格寬度
        $objPHPExcel->getActiveSheet()->getColumnDimension('A')->setAutoSize(true);
        $objPHPExcel->getActiveSheet()->getColumnDimension('B')->setWidth(50);
        
        /**
         * 左對齊與 右對齊
         * 可設置整列->getStyle('N')   可針對行rows設置getStyle('N3')
         */
        $objPHPExcel->getActiveSheet()->getStyle('B')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
        $objPHPExcel->getActiveSheet()->getStyle('B3')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);

        echo date('H:i:s'), " 設置隱藏C D列", EOL;
        $objPHPExcel->getActiveSheet()->getColumnDimension('C')->setVisible(false);
        $objPHPExcel->getActiveSheet()->getColumnDimension('D')->setVisible(false);

        echo date('H:i:s'), " 設置大綱級別", EOL;
        $objPHPExcel->getActiveSheet()->getColumnDimension('E')->setOutlineLevel(1)
                ->setVisible(false)
                ->setCollapsed(true);

        /**
         * 開始添加數據
         */
        for ($i = 2; $i <= 50000; $i++) {
            $objPHPExcel->getActiveSheet()->setCellValue('A' . $i, "FName $i")
                    ->setCellValue('B' . $i, "LName $i")
                    ->setCellValue('C' . $i, "PhoneNo $i")
                    ->setCellValue('D' . $i, "FaxNo $i")
                    ->setCellValue('E' . $i, true);
        }

        $objPHPExcel->getActiveSheet()->setTitle('供應商信息');
        echo date('H:i:s'), " 設置格式為Excel2007版格式", EOL;
        $callStartTime = microtime(true);

        $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
        $objWriter->save(str_replace('.php', '.xlsx', __FILE__));
        $callEndTime = microtime(true);
        $callTime = $callEndTime - $callStartTime;

        echo date('H:i:s'), " 設置生成的文件為: ", str_replace('.php', '.xlsx', pathinfo(__FILE__, PATHINFO_BASENAME)), EOL;
        echo date('H:i:s'), ' 寫入Workbook中耗時 ', sprintf('%.4f', $callTime), " 秒", EOL;
        echo date('H:i:s'), ' 當前內存使用情況: ', (memory_get_usage(true) / 1024 / 1024), " MB", EOL;
        echo date('H:i:s'), " 內存使用峰值: ", (memory_get_peak_usage(true) / 1024 / 1024), " MB", EOL;
        echo date('H:i:s'), " 完成寫入文件", EOL;
        echo date('H:i:s'), ' 文件被創建在: ', getcwd(), '目錄', EOL;

 

 

下面模擬一個大數據的導出:

msyql中tcustomer表有數據4萬多條

我們設置

set_time_limit(0);
ini_set("memory_limit", "1024M"); 
\PHPExcel_CachedObjectStorageFactory::cache_in_memory_gzip;  # 單元格緩存為MemoryGZip

然后導出所有4萬多條數據

代碼如下(Yii2)

/**
     * PHPExcel 數據導出
     */
    public function actionPhpexcel() {

        define('EOL', '<br />');
        $objPHPExcel = new \app\extensions\PHPExcel\PHPExcel();
        ini_set("memory_limit", "1024M"); // 設置php可使用內存
        set_time_limit(0);  # 設置執行時間最大值

        $cacheMethod = \PHPExcel_CachedObjectStorageFactory::cache_in_memory_gzip;
        if (!\PHPExcel_Settings::setCacheStorageMethod($cacheMethod)) {
            die($cacheMethod . " 緩存方法不可用" . EOL);
        }
        echo date('H:i:s'), " 當前使用的緩存方法是: ", $cacheMethod, " 方式", EOL;
        echo date('H:i:s'), " 開始設置文檔屬性", EOL;
        $objPHPExcel->getProperties()->setCreator("dcb3688")
                ->setLastModifiedBy("dcb3688")
                ->setTitle("客戶信息記錄")
                ->setSubject("客戶信息Document")
                ->setDescription("描述……")
                ->setKeywords("office 2007  php")
                ->setCategory("產品信息AAA");


        echo date('H:i:s'), " 開始添加單元格標題", EOL;
        $objPHPExcel->setActiveSheetIndex(0);
        $objPHPExcel->getActiveSheet()->setCellValue('A1', "客戶姓名");
        $objPHPExcel->getActiveSheet()->setCellValue('B1', "性別");
        $objPHPExcel->getActiveSheet()->setCellValue('C1', "Province");
        $objPHPExcel->getActiveSheet()->setCellValue('D1', "City");
        $objPHPExcel->getActiveSheet()->setCellValue('E1', "Town");
        $objPHPExcel->getActiveSheet()->setCellValue('F1', "Telephone");
        $objPHPExcel->getActiveSheet()->setCellValue('G1', "屬相");
        $objPHPExcel->getActiveSheet()->setCellValue('H1', "星座");
        $objPHPExcel->getActiveSheet()->setCellValue('I1', "血型");
        $objPHPExcel->getActiveSheet()->setCellValue('J1', "Nid");
        $objPHPExcel->getActiveSheet()->setCellValue('K1', "Uid");
        $objPHPExcel->getActiveSheet()->setCellValue('L1', "Etime");
        $objPHPExcel->getActiveSheet()->setCellValue('M1', "Regtime");
        $objPHPExcel->getActiveSheet()->setCellValue('N1', "Signup");
        $objPHPExcel->getActiveSheet()->setCellValue('O1', "經度");
        $objPHPExcel->getActiveSheet()->setCellValue('P1', "緯度");
        $objPHPExcel->getActiveSheet()->setCellValue('Q1', "類型");
        $objPHPExcel->getActiveSheet()->setCellValue('R1', "狀態");

        /**
         * 單元格寬度
         */
        $objPHPExcel->getActiveSheet()->getColumnDimension('F')->setAutoSize(true);
        $objPHPExcel->getActiveSheet()->getColumnDimension('J')->setWidth(45);
        $objPHPExcel->getActiveSheet()->getColumnDimension('L')->setAutoSize(true);
        $objPHPExcel->getActiveSheet()->getColumnDimension('M')->setAutoSize(true);
        $objPHPExcel->getActiveSheet()->getColumnDimension('N')->setAutoSize(true);
        
        /**
         * 左對齊與 右對齊
         * 可設置整列->getStyle('N')   可針對行rows設置getStyle('N3')
         */
        $objPHPExcel->getActiveSheet()->getStyle('N')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_RIGHT);
        $objPHPExcel->getActiveSheet()->getStyle('N3')->getAlignment()->setHorizontal(\PHPExcel_Style_Alignment::HORIZONTAL_LEFT);





        #####################################開始添加數據###############################################################
        
        /**
         * 分頁分時間: 微軟Execl最大值6萬行, total/60000=文件個數,  limit 60000, 60000
         * $model->find()->offset($pages->offset)->limit(60000)->all()
         */
        $model = \app\models\Tcustomer::find()->orderBy('regtime desc')->all();
        if (!empty($model)) {
            foreach ($model as $key => $value) {
                $Cellkey = $key + 2;
                $blood = [1 => 'A型', 2 => 'B型', 3 => 'AB型', 4 => 'O型'];
                $objPHPExcel->getActiveSheet()->setCellValue('A' . $Cellkey, mb_substr($value->realname, 0, -1) . '*');
                $objPHPExcel->getActiveSheet()->setCellValue('B' . $Cellkey, rand(1, 2) == 1 ? '先生' : '女士');
                $objPHPExcel->getActiveSheet()->setCellValue('C' . $Cellkey, $value->province);
                $objPHPExcel->getActiveSheet()->setCellValue('D' . $Cellkey, $value->city);
                $objPHPExcel->getActiveSheet()->setCellValue('E' . $Cellkey, $value->town);
                $objPHPExcel->getActiveSheet()->setCellValue('F' . $Cellkey, $value->telephone ? substr($value->telephone, 0, 3) . '*********' : '');
                $objPHPExcel->getActiveSheet()->setCellValue('G' . $Cellkey, $value->sx);
                $objPHPExcel->getActiveSheet()->setCellValue('H' . $Cellkey, $value->constel);
                $objPHPExcel->getActiveSheet()->setCellValue('I' . $Cellkey, $blood[array_rand($blood)]);
                $objPHPExcel->getActiveSheet()->setCellValue('J' . $Cellkey, mb_substr($value->nid, 0, -3));
                $objPHPExcel->getActiveSheet()->setCellValue('K' . $Cellkey, $value->uid);
                $objPHPExcel->getActiveSheet()->setCellValue('L' . $Cellkey, $value->exp);
                $objPHPExcel->getActiveSheet()->setCellValue('M' . $Cellkey, $value->regtime);
                $objPHPExcel->getActiveSheet()->setCellValue('N' . $Cellkey, $value->signup ? $value->signup : '無數據');
                $objPHPExcel->getActiveSheet()->setCellValue('O' . $Cellkey, substr($value->lng, 0, 4) . rand(10000, 999999));
                $objPHPExcel->getActiveSheet()->setCellValue('P' . $Cellkey, substr($value->lat, 0, 3) . rand(10000, 999999));
                $objPHPExcel->getActiveSheet()->setCellValue('Q' . $Cellkey, $value->type == 1 ? '意向客戶' : '觀望中客戶');
                $objPHPExcel->getActiveSheet()->setCellValue('R' . $Cellkey, $value->status == 1 ? '已下單' : '已跟進');
            }
        } else {
            die(" 暫無數據" . EOL);
        }

        #######################################################################################################

        $objPHPExcel->getActiveSheet()->setTitle('客戶信息');
        echo date('H:i:s'), " 設置格式為Excel2007版格式", EOL;
        $callStartTime = microtime(true);

        $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
        $objWriter->save(str_replace('.php', '.xlsx', __FILE__));
        $callEndTime = microtime(true);
        $callTime = $callEndTime - $callStartTime;

        echo date('H:i:s'), " 設置生成的文件為: ", str_replace('.php', '.xlsx', pathinfo(__FILE__, PATHINFO_BASENAME)), EOL;
        echo date('H:i:s'), ' 寫入Workbook中耗時 ', sprintf('%.4f', $callTime), " 秒", EOL;
        echo date('H:i:s'), ' 當前內存使用情況: ', (memory_get_usage(true) / 1024 / 1024), " MB", EOL;
        echo date('H:i:s'), " 內存使用峰值: ", (memory_get_peak_usage(true) / 1024 / 1024), " MB", EOL;
        echo date('H:i:s'), " 完成寫入文件", EOL;
        echo date('H:i:s'), ' 文件被創建在: ', getcwd(), '目錄', EOL;
    }

執行后信息:

打開文件:

 

 


免責聲明!

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



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