PHP 导出及导入CSV大文件

2,157 阅读2分钟

在百度查php导出excel资料的时候,无意间发现,还有php导出csv文件的功能。

他们二者没有什么区别。具体看需求。

但是要操作的文件特别大的时候,推荐使用csv。

导出csv的时候,需要用到一些php原生函数。我百度了一下基本用法。附上链接

Fgetcsv()
参考:http://www.w3school.com.cn/php/func_filesystem_fgetcsv.asp
Fputcsv()
参考:http://www.w3school.com.cn/php/func_filesystem_fputcsv.asp
Fopen()
参考:http://www.w3school.com.cn/php/func_filesystem_fputcsv.asp
Iconv()
参考:http://php.net/manual/fr/function.iconv.php

先是导出:两种方法:

使用 header

我这里使用的是thinkphp3.2框架

    /**
     * 导出csv
     */
      public function putCsv(){
        $tableheader array('姓名''性别''年龄''学院''班级');
        $tablelength count($tableheader);
 
        /*表格数据*/
        $data M()->query("SELECT id,username,logtime,gid,ip FROM admin");
 
        /*输入到CSV文件 解决乱码问题*/
        $html "xEFxBBxBF";
 
        /*输出表头*/
        foreach ($tableheader as $value) {
            $html .= $value "	 ,";
        }
        $html .= "
";
 
        /*输出内容*/
        foreach ($data as $value) {
            for ($i 0$i $tablelength$i++) {
                $html .= $value[$i] . "	 ,";
            }
            $html .= $value['createtime'] . "	 ,";
            $html .= "
";
        }
 
        /*输出CSV文件*/
        header("Content-type:text/csv");
        header("Content-Disposition:attachment; filename=全部数据.csv");
        echo $html;
        exit();
      }

使用上边那些函数:

   /**
     * 重写fputcsv方法,添加转码功能
     * @param $handle
     * @param array $fields
     * @param string $delimiter
     * @param string $enclosure
     * @param string $escape_char
     */
    function fputcsv2($handlearray $fields$delimiter ","$enclosure '"'$escape_char "\") {
        foreach ($fields as $k => $v) {
            $fields[$k] = iconv("UTF-8", "GB2312//IGNORE", $v);  // 这里将UTF-8转为GB2312编码
        }
        fputcsv($handle$fields$delimiter$enclosure$escape_char);
    }
    /**
     * 导出csv
     */
    public function csv()
    {
        set_time_limit(0);
        ini_set('memory_limit', '128M');//设置文件最大限制
        $fileName = date('YmdHis', time());//文件名
        // 设置头部
        header('Content-Encoding: UTF-8');
        header("Content-type:application/vnd.ms-excel;charset=UTF-8");
        header('Content-Disposition: attachment;filename="' . $fileName . '.csv"');
        //注意,数据量在大的情况下。比如导出几十万到几百万,
        //会出现504 Gateway Time-out,请修改php.ini的max_execution_time参数
        //打开php标准输出流以写入追加的方式打开
        $fp = fopen('php://output', 'a');
       
       
        //设置标题
        $title = array('id', '编号', '姓名', '年龄');
        //注意这里是小写id,否则ID命名打开会提示Excel 已经检测到"xxx.xsl"是SYLK文件,但是不能将其加载: CSV 文或者XLS文件的前两个字符是大写字母"I","D"时,会发生此问题。
        foreach ($title as $key => $item){
            $title[$key] = iconv("UTF-8", "GB2312//IGNORE", $item);
        }
       
        fputcsv2($fp$title);
       
        // 导出百万级别数据时才需要用到for循环
        //用fputcsv从数据库中导出1百万的数据,比如我们每次取1万条数据,分100步来执行
        //一次性读取1万条数据,也可以把$nums调小,$step相应增大。
        //$step = 100;
        //$nums = 10000;
        // for ($s = 1; $s <= $step$s++) {
            // $start = ($s - 1) * $nums;
            $result = M()->query("SELECT id,username,logtime,gid FROM admin");
 
            foreach($result as $key=>$row){
                foreach ($row as $key => $item){
                    $row[$key] = iconv("UTF-8", "GBK", $item); //这里必须转码,不然会乱码
                }
                   
                fputcsv($fp$row);
            }
            //ob_flush();  //每1万条数据就刷新缓冲区
            //flush();
        // }
    }

使用 header 导出这个就没有可说的了。

重点说下下边这个。

上边我注释掉的所有代码都是有用的,当你导出的数据量特别大的时候。

上边的测试是以百万级别的。

具体的,上边都有注释。看不明白的,请在下方留言。

下边是导入:

/**
     * 导入csv
     */
    public function incsv()
    {
        $data $this->csv_get_lines('./uploads/csv/20181221090925.csv'100);
        echo "<pre>";
        var_dump($data);
           // 加下来是将返回数组的数据插入数据库。这部分不做测试。
    }
 
    /**
     * csv_get_lines 读取CSV文件中的某几行数据
     * @param $csvfile csv文件路径
     * @param $lines 读取行数
     * @param $offset 起始行数
     * @return array
     * */
    public function csv_get_lines($csvfile$lines$offset 0)
    {
        if(!$fp fopen($csvfile'r')) {
            return false;
        }
        $i $j 0;
        while (false !== ($line fgets($fp))) {
            if($i++ < $offset) {
                continue;
            }
            break;
        }
        $data array();
        while(($j++ < $lines) && !feof($fp)) {
            $info fgetcsv($fp);
            foreach ($info as $key => $item){
                $info[$key] = iconv("GBK""UTF-8"$item); //这里必须转码,不然会乱码
            }
            $data[] = $info;
        }
        fclose($fp);
        return $data;
}

导入这个着重说一下参数。

$lines 读取行数

$offset 起始行数

也就是说,从哪开始读取,读取到哪里你是可以控制的,这样方便了大文件的导出。

百万级的文件,分一百次导入,一次导入一万条。我觉得要比一次直接导入一百万条数据要好。

有好的建议,请在下方输入评论

欢迎访问个人博客 guanchao.site

欢迎访问我的小程序:打开微信->发现->小程序->搜索“时间里的”