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

对于许多的从事数据智能开发的同僚来说,从库中提取出数据后进行数据整理并且导出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()方法中不要使用路径变量拼接,最好在使用该方法前就写好路径。使用了拼接不会报错,但是依然会文件添加不进去,这里是一个大坑,我找了好久才发现。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容