首頁  >  文章  >  後端開發  >  php csv 匯出

php csv 匯出

不言
不言原創
2018-04-20 10:04:392623瀏覽



這篇文章介紹的內容是關於php csv 匯出,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下


概述:


最近公司專案要求把資料除了頁面輸出也希望有匯出功能,雖然之前也做過幾個導出功能,但這次數據量相對比較大,差不多一天數據就20W條,要求導7天或者30天,那麼數據量就輕鬆破百萬了甚至破千萬,因此開發的過程中發現了一些大數據導出的坑,在此跟大家分享一下,互相學習。

準備:

1、PHP設定坑:

  • set_time_limit – 設定腳本最大執行時間:

此配置一般PHP預設是30秒,如果你是資料小的,可能就不會發現有該設定問題,但如果你資料達到了百萬級導出,往往30秒是不夠的,因此你需要在你的腳本中加入set_time_limit(0),讓該腳本沒有執行時間現在

  • #memory_limit – PHP的記憶體限定:

這個配置一般php預設是128M,如果之前做過小數據的朋友可能也會動過這個配置就能解決許多問題,或許有人想,你大數據也把這個調大不就行了嗎?那麼真的是too young too native了,你本地能設置1G或者無限製或許真的沒問題,但是正式場,你這麼搞遲早會出事的,一個PHP程序佔那麼大的內存的空間,如果你叫你公司維幫忙調一下配置,估計運維一定很不情願,伺服器硬體這麼搞也是太奢侈了。所以說,我們要盡量避免調大該設定。

2、excel坑:

既然是導出數據,大夥們當然馬上想到了excel格式了,多方便查看數據呀,然而萬萬沒想到excel也是有脾氣的呀!

  • 表格資料限制:

Excel 2003及以下的版本。一张表最大支持65536行数据,256列。
Excel 2007-2010版本。一张表最大支持1048576行,16384列。
  • 1

  • ##2

也就是說你想幾百萬個輕輕鬆鬆一次導入一張EXCEL表是不行的,你起碼需要進行資料分割,保證資料不能超過104W一張表。

  • PHPexcel記憶體溢出:

既然資料限制在104W,那麼資料分割就資料分割唄,於是你嘗試50W一次導入表,然而PHPexcel內部有函數報內存溢位錯誤,然後你就不斷的調小資料量,直到5W一次導入你都會發現有記憶體溢位錯誤。這是為什麼呢,雖然你分割數據來導入多個數據表,但是最後PHPexcel內部還是一次性把所有表數據放進一個變量中來創建文件……額,這幾百萬數據一個變量存儲,你想記憶體不溢出,真是有點困難。

(後來看了一些文章發現PHPExcel也有解決方案,PHPExcel_Settings::setCacheStorageMethod方法更改緩衝方式來減少記憶體的使用)

3、csv坑:

EXCEL這麼麻煩,我不用還不行嗎?我用csv檔案儲存,既不限制數量,還能直接用EXCEL來查看,又能以後把文件導入資料庫,一舉幾得豈不是美哉?咦,少俠好點子!但是CSV也有坑哦!

  • 輸出buffer太多:

當你用PHP原生函式putcsv()其實就用到了輸出快取buffer,如果你把幾百萬的資料一直用這個函數輸出,會導致輸出快取太大而報錯的,因此我們每隔一定量的時候,必須進行將輸出快取中的內容取出來,設定為等待輸出狀態。具體操作是:

ob_flush();flush();

  • 1

  • 2

具体说明介绍:PHP flush()与ob_flush()的区别详解

  • EXCEL查看CSV文件数量限制:

大多数人看csv文件都是直接用EXCEL打开的。额,这不就是回到EXCEL坑中了吗?EXCEL有数据显示限制呀,你几百万数据只给你看104W而已。什么?你不管?那是他们打开方式不对而已?不好不好,我们解决也不难呀,我们也把数据分割一下就好了,再分开csv文件保存,反正你不分割数据变量也会内存溢出。

4、总结做法

分析完上面那些坑,那么我们的解决方案来了,假设数据量是几百万。

1、那么我们要从数据库中读取要进行数据量分批读取,以防变量内存溢出,

2、我们选择数据保存文件格式是csv文件,以方便导出之后的阅读、导入数据库等操作。

3、以防不方便excel读取csv文件,我们需要104W之前就得把数据分割进行多个csv文件保存

4、多个csv文件输出给用户下载是不友好的,我们还需要把多个csv文件进行压缩,最后提供给一个ZIP格式的压缩包给用户下载就好。

代码:

 //导出说明:因为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 . &#39;_&#39; . $i . &#39;.csv&#39;, &#39;w&#39;); //生成临时文件
      //     chmod(&#39;attack_ip_info_&#39; . $i . &#39;.csv&#39;,777);//修改可执行权限
            $fileNameArr[] = $mark . &#39;_&#39; .  $i . &#39;.csv&#39;;        // 将数据通过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(&#39;Content-disposition: attachment; filename=&#39; . basename($filename)); // 文件名
        header("Content-Type: application/zip"); // zip格式的
        header("Content-Transfer-Encoding: binary"); //
        header(&#39;Content-Length: &#39; . filesize($filename)); //
        @readfile($filename);//输出文件;
        unlink($filename); //删除压缩包临时文件
    }
  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10

  • 11

  • 12

  • 13

  • 14

  • 15

  • 16

  • 17

  • 18

  • 19

  • 20

  • 21

  • 22

  • 23

  • 24

  • 25

  • 26

  • 27

  • 28

  • 29

  • 30

  • 31

  • 32

  • 33

  • 34

  • 35

  • 36

  • 37

  • 38

  • 39

  • 40

  • 41

  • 42

  • 43

  • 44

  • 45

  • 46

  • 47

  • 48

  • 49

  • 50

  • 51

  • 52

  • 53

  • 54

  • 55

  • 56

  • 57

  • 58

总结:

其实上面代码还是有优化的空间的,比如说用异常捕捉,以防因为某些错误而导致生成了一些临时文件又没有正常删除,还有PHPexcel的缓存设置也许能解决内存溢出问题,可以生成一个EXCEL文件多个工作表的形式,这样对于文件阅读者来说更友好。

以上便是本人对PHP大数据导出的见解,希望能帮到您们,同时不足的地方请多多指教!

———————————————————————————————————— 
2017年12月17日 
PS:最近了解其实关于内存溢出的问题,用迭代器来处理会方便多了。

版权声明:每一篇原创文章都是我的心血,欢迎转载,但请转载前留个评论,感谢您的支持!!! https://blog.csdn.net/Tim_phper/article/details/77581071

相关推荐:

php 逐行读取csv数据入库

PHP进行读取CSV文件数据和生成CSV文件

以上是php csv 匯出的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn