美文网首页PHP首页投稿(暂停使用,暂停投稿)程序员
PHP学习笔记---实现导出csv功能(附带打包zip教程)

PHP学习笔记---实现导出csv功能(附带打包zip教程)

作者: PeTu | 来源:发表于2016-07-11 13:21 被阅读655次

    对于许多的从事数据智能开发的同僚来说,从库中提取出数据后进行数据整理并且导出csv文件的功能是很常见的,导出一个csv文件方便用其他的数据工具进行分析。所以在这里分享一下我在工作过程中实现导出csv文件功能的历程与所获。

    前言

    首先,我被告知需要在laravel框架中实现下载接口这个任务时,整个人是懵逼的,完全不知道怎么着手去实现这个功能,但是对于一个理工科生来说,碰到问题并不可怕,剥丝抽茧,一步一步来。我分析,既然要实现下载功能接口,首先需要做的就是提供一个接口,而如何做一个接口我在<<Laravel使用心得--简易路由操作>>中已介绍,向前端提供一个URI即达到了接口的意义,其次是如何实现下载,最后是如何写入一个csv文件,本篇文章就从后两个方向介绍,并且最后附带PHP中文件打包功能的实现介绍

    本来想打包功能单独写一篇博客的,后来发现这个功能实现比较简单,而深层次的我也暂时不会,就附带本篇文章最后了

    下载

    一、通过传递HTTP报头实现下载

    首先在度娘上找到的实现下载的方式之一:是通过向浏览器传递HTTP报头,告诉浏览器这个URI的相关动作让浏览器去实现。
    HTTP报头是HTTP协议的一个部分,一般上用于客户端和服务端之间握手时的通信,通俗的理解就是 http服务器和客户端(一般为浏览器)之间数据传输之前的对话,告诉浏览器你想干什么。
    而在PHP中实现HTTP报头参数传递功能的是header()方法,header() 函数向客户端发送原始的 HTTP 报头。其中认识到一点很重要,即必须在任何实际的输出被发送之前调用 header() 函数,例如在调用header()函数前不要写print_r()或var_dump()等函数。
    传递报头参数的代码:

    header("Content-type:text/csv");
    header("Content-Disposition:attachment;filename=" . $start_date . '~' . $end_date . '_fare.csv');
    header('Cache-Control:must-revalidate,post-check=0,pre-check=0');
    header('Expires:0');
    header('Pragma:public');
    

    其中第一行是告诉浏览器我需要导出文件,格式是csv,在Content-type这个参数类型中可以指定许多的导出文本的格式,例如rar、zip这样的压缩包格式
    传递这样的报头后,导出的文件的内容将是你在调用该header()函数的方法内的return值,例如return 123;则csv文件中就是123。
    这种方式可以实现下载,但是总归看上去不太好看,如此优秀的laravel框架怎么可能会不涉及到下载方法的封装呢,于是后来使用了另一种方法。

    二、通过response方法实现下载

    在看了其他前辈写的代码中,我发现有一行代码

    return reponse()->download($file)
    

    看单词意思也知道这行代码是起什么作用的。Response是laravel框架中的门面(facade),在这个框架中是可以直接引用调用的功能
    例如:

    //响应重定向
    Route::get('example/test24', function(){
        return Redirect::to('example/test21')->with('username', 'xiaoming');
    });
    //定制HTTP响应
    Route::get('example/test21', function(){
        return Response::make('内容不存在', 404);
    });
    //响应视图
    Route::get('example/test22', function(){
        return Response::view('test22');
    });
    

    以上的例子是response在封闭函数中的直接调用,在其他的地方自然也是可以直接使用的,而下载文件就可以使用Response::download()方法
    我们先看一下这个的源代码:

        /**
         * Create a new file download response.
         *
         * @param  \\SplFileInfo|string  $file
         * @param  string  $name
         * @param  array  $headers
         * @param  string|null  $disposition
         * @return \\Symfony\\Component\\HttpFoundation\\BinaryFileResponse
         */
        public function download($file, $name = null, array $headers = [], $disposition = 'attachment');
    

    可以看到这个下载方法的参数,有$file, $name = null, array $headers = [], $disposition = 'attachment',后面都有默认值,可以不传递,也可以用于参数扩展,利用这个方法就可以实现下载
    例如:

    public function getDownload()
    {
        //PDF file is stored under project/public/download/info.pdf
        $file= public_path(). "/download/info.pdf";
        $headers = array(
                  'Content-Type: application/pdf',
                );
        return Response::download($file, 'filename.pdf', $headers);
    }
    

    由此处的header()可以看出是要求下载一个pdf文件,而在laravel 5框架中使用此功能还可以使用

    return response()->download($file, 'filename.pdf', $headers);
    

    这种方式,功能是一样的,其中也可以只指定第一个参数,这样下载的文件就是你的文件之前指定好的类型。

    写入csv文件方法

    介绍了如何实现下载的两种方法,现在来说一下如何将数据写入csv文件

    字符串连接方法

    首先要知道,csv文件的内容其实就是一串拼接起来的字符串,起始指定好表头字符串,后面就以该表头的顺序依次拼接数据即可,只是在表头和每一行数据的末尾都添加一个换行符\\n来达到表格对齐的效果即可。
    例如:

    $head_str = "日期,姓名,年龄,学校\\n";
    $cnt  = count($data);
    for ($i =0;$i<$cnt;$i++) {
            $tmp = implode(",",$data[$i]);
            $head_str .= $tmp."\\n";
    }
    

    其中$data是从库中取出的数据的二维数组,而每一个第一层索引指向的就是对应的每一行数据,然后利用for循环遍历取出每一行数据进行拼接。
    这一种方法是和传递HTTP报头实现下载的方法配合使用效果更好,因为在拼接完成后直接在方法内return $head_str,就能将整个数据内容读入到了下载的csv文件中。当然,也可以使用fwrite()方法写入一个新文件$file,然后利用response->download($file)方法下载该文件即可。

    export()方法

    后来发现,每次这样拼接数据非常的麻烦,可以写一个公共的方法,以便在其他地方实现类似的功能时可以直接调用

        public static function exportData($data = array(), $title = [])
        {
            $new_data = [];
            if (!empty($data)) {
                if(empty($title))
                {
                    foreach ($data as $key => $val) {
                        $new_data[$key] = isset($val) ?   mb_convert_encoding($val, 'gbk', 'utf-8') : '';
                    }
                } else {
                    foreach ($title as $key => $val) {
                        $new_data[$key] = isset($data[$key]) ? mb_convert_encoding($data[$key], 'gbk', 'utf-8') : '';
                    }
                }
                $str =  implode(',', $new_data);
                fwrite(self::$fp, $str."\\n");
            }
        }
    

    这个方法的实现原理是将数据进行转码处理然后利用fwrite()方法写入一个新文件。其中self::$fp是指定的文件的路径,这个php手册上看一下fwrite()方法的介绍就能晓得参数的意思。写入了新的文件后就可以再通过reponse->download()方法来下载了。

    php文件打包教程

    在数据量非常庞大时,一次性取出大量的数据然后写入csv文件再下载的这个流程是不适用的,因为数据量庞大会导致取数时间很长,命令运行超出内存。此时可以采用的方法就是将大量的数据按时间维度写入多个csv文件,然后再根据需要的时间区间将多个csv文件打包下载即可,所以在这也讲一下我如何实现文件打包。
    在php中,利用的是ZipArchive()类,通过这个类的实例化来实现打包。

    依然是感兴趣的同学自行在php手册上学习

    代码:

    //获取zip包名
    $zip_file = $save_path . '/' . $start_date . '-' . $end_date . '.zip';
    if (file_exists($zip_file)) {
            return response()->download($zip_file);
    }
    //文件打包
    $zip = new ZipArchive();
    if ($zip -> open($zip_file, ZipArchive::CREATE) == true) {
            foreach ($file_dir as $file) {
                 if (file_exists($file)) {
                 $zip -> addFile($file, basename($file));
                 }
            }
    }
    $zip -> close();
    

    这样就实现了打包,其中$file_dir变量是你要打包的文件的路径数组,里面包含所有你想打包的文件路径,$zip_file变量是你想打包成zip文件的包的路径+名称。

    有的同学在使用此方法时有时会不管用,以我的经验,一般都是文件的路径不正确,或者是没有指定绝对路径
    注意:在$zip -> addFile()方法中不要使用路径变量拼接,最好在使用该方法前就写好路径。使用了拼接不会报错,但是依然会文件添加不进去,这里是一个大坑,我找了好久才发现。

    最后:本人新手程序员,一起进步!!!

    相关文章

      网友评论

        本文标题:PHP学习笔记---实现导出csv功能(附带打包zip教程)

        本文链接:https://www.haomeiwen.com/subject/vjcjjttx.html