對于許多的從事數(shù)據(jù)智能開發(fā)的同僚來說,從庫中提取出數(shù)據(jù)后進(jìn)行數(shù)據(jù)整理并且導(dǎo)出csv文件的功能是很常見的准谚,導(dǎo)出一個(gè)csv文件方便用其他的數(shù)據(jù)工具進(jìn)行分析。所以在這里分享一下我在工作過程中實(shí)現(xiàn)導(dǎo)出csv文件功能的歷程與所獲。
前言
首先,我被告知需要在laravel框架中實(shí)現(xiàn)下載接口這個(gè)任務(wù)時(shí)满败,整個(gè)人是懵逼的,完全不知道怎么著手去實(shí)現(xiàn)這個(gè)功能叹括,但是對于一個(gè)理工科生來說算墨,碰到問題并不可怕,剝絲抽繭汁雷,一步一步來净嘀。我分析,既然要實(shí)現(xiàn)下載功能接口侠讯,首先需要做的就是提供一個(gè)接口挖藏,而如何做一個(gè)接口我在<<Laravel使用心得--簡易路由操作>>中已介紹,向前端提供一個(gè)URI即達(dá)到了接口的意義厢漩,其次是如何實(shí)現(xiàn)下載膜眠,最后是如何寫入一個(gè)csv文件,本篇文章就從后兩個(gè)方向介紹溜嗜,并且最后附帶PHP中文件打包功能的實(shí)現(xiàn)介紹
本來想打包功能單獨(dú)寫一篇博客的宵膨,后來發(fā)現(xiàn)這個(gè)功能實(shí)現(xiàn)比較簡單,而深層次的我也暫時(shí)不會炸宵,就附帶本篇文章最后了
下載
一辟躏、通過傳遞HTTP報(bào)頭實(shí)現(xiàn)下載
首先在度娘上找到的實(shí)現(xiàn)下載的方式之一:是通過向?yàn)g覽器傳遞HTTP報(bào)頭,告訴瀏覽器這個(gè)URI的相關(guān)動作讓瀏覽器去實(shí)現(xiàn)土全。
HTTP報(bào)頭是HTTP協(xié)議的一個(gè)部分捎琐,一般上用于客戶端和服務(wù)端之間握手時(shí)的通信,通俗的理解就是 http服務(wù)器和客戶端(一般為瀏覽器)之間數(shù)據(jù)傳輸之前的對話涯曲,告訴瀏覽器你想干什么野哭。
而在PHP中實(shí)現(xiàn)HTTP報(bào)頭參數(shù)傳遞功能的是header()方法,header() 函數(shù)向客戶端發(fā)送原始的 HTTP 報(bào)頭幻件。其中認(rèn)識到一點(diǎn)很重要,即必須在任何實(shí)際的輸出被發(fā)送之前調(diào)用 header() 函數(shù)蛔溃,例如在調(diào)用header()函數(shù)前不要寫print_r()或var_dump()等函數(shù)绰沥。
傳遞報(bào)頭參數(shù)的代碼:
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');
其中第一行是告訴瀏覽器我需要導(dǎo)出文件,格式是csv贺待,在Content-type
這個(gè)參數(shù)類型中可以指定許多的導(dǎo)出文本的格式徽曲,例如rar、zip這樣的壓縮包格式
傳遞這樣的報(bào)頭后麸塞,導(dǎo)出的文件的內(nèi)容將是你在調(diào)用該header()函數(shù)的方法內(nèi)的return值秃臣,例如return 123;
則csv文件中就是123。
這種方式可以實(shí)現(xiàn)下載,但是總歸看上去不太好看奥此,如此優(yōu)秀的laravel框架怎么可能會不涉及到下載方法的封裝呢弧哎,于是后來使用了另一種方法。
二稚虎、通過response方法實(shí)現(xiàn)下載
在看了其他前輩寫的代碼中撤嫩,我發(fā)現(xiàn)有一行代碼
return reponse()->download($file)
看單詞意思也知道這行代碼是起什么作用的。Response是laravel框架中的門面(facade)蠢终,在這個(gè)框架中是可以直接引用調(diào)用的功能
例如:
//響應(yīng)重定向
Route::get('example/test24', function(){
return Redirect::to('example/test21')->with('username', 'xiaoming');
});
//定制HTTP響應(yīng)
Route::get('example/test21', function(){
return Response::make('內(nèi)容不存在', 404);
});
//響應(yīng)視圖
Route::get('example/test22', function(){
return Response::view('test22');
});
以上的例子是response在封閉函數(shù)中的直接調(diào)用序攘,在其他的地方自然也是可以直接使用的,而下載文件就可以使用Response::download()
方法
我們先看一下這個(gè)的源代碼:
/**
* 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');
可以看到這個(gè)下載方法的參數(shù)寻拂,有$file, $name = null, array $headers = [], $disposition = 'attachment'
程奠,后面都有默認(rèn)值,可以不傳遞祭钉,也可以用于參數(shù)擴(kuò)展梦染,利用這個(gè)方法就可以實(shí)現(xiàn)下載
例如:
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()可以看出是要求下載一個(gè)pdf文件,而在laravel 5框架中使用此功能還可以使用
return response()->download($file, 'filename.pdf', $headers);
這種方式朴皆,功能是一樣的帕识,其中也可以只指定第一個(gè)參數(shù),這樣下載的文件就是你的文件之前指定好的類型遂铡。
寫入csv文件方法
介紹了如何實(shí)現(xiàn)下載的兩種方法肮疗,現(xiàn)在來說一下如何將數(shù)據(jù)寫入csv文件
字符串連接方法
首先要知道,csv文件的內(nèi)容其實(shí)就是一串拼接起來的字符串扒接,起始指定好表頭字符串伪货,后面就以該表頭的順序依次拼接數(shù)據(jù)即可,只是在表頭和每一行數(shù)據(jù)的末尾都添加一個(gè)換行符\\n
來達(dá)到表格對齊的效果即可钾怔。
例如:
$head_str = "日期,姓名,年齡,學(xué)校\\n";
$cnt = count($data);
for ($i =0;$i<$cnt;$i++) {
$tmp = implode(",",$data[$i]);
$head_str .= $tmp."\\n";
}
其中$data
是從庫中取出的數(shù)據(jù)的二維數(shù)組碱呼,而每一個(gè)第一層索引指向的就是對應(yīng)的每一行數(shù)據(jù),然后利用for
循環(huán)遍歷取出每一行數(shù)據(jù)進(jìn)行拼接宗侦。
這一種方法是和傳遞HTTP報(bào)頭實(shí)現(xiàn)下載的方法配合使用效果更好愚臀,因?yàn)樵谄唇油瓿珊笾苯釉诜椒▋?nèi)return $head_str
,就能將整個(gè)數(shù)據(jù)內(nèi)容讀入到了下載的csv文件中矾利。當(dāng)然姑裂,也可以使用fwrite()
方法寫入一個(gè)新文件$file
,然后利用response->download($file)
方法下載該文件即可男旗。
export()方法
后來發(fā)現(xiàn)舶斧,每次這樣拼接數(shù)據(jù)非常的麻煩,可以寫一個(gè)公共的方法察皇,以便在其他地方實(shí)現(xiàn)類似的功能時(shí)可以直接調(diào)用
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");
}
}
這個(gè)方法的實(shí)現(xiàn)原理是將數(shù)據(jù)進(jìn)行轉(zhuǎn)碼處理然后利用fwrite()
方法寫入一個(gè)新文件茴厉。其中self::$fp
是指定的文件的路徑,這個(gè)php手冊上看一下fwrite()
方法的介紹就能曉得參數(shù)的意思。寫入了新的文件后就可以再通過reponse->download()
方法來下載了矾缓。
php文件打包教程
在數(shù)據(jù)量非常龐大時(shí)怀酷,一次性取出大量的數(shù)據(jù)然后寫入csv文件再下載的這個(gè)流程是不適用的,因?yàn)閿?shù)據(jù)量龐大會導(dǎo)致取數(shù)時(shí)間很長而账,命令運(yùn)行超出內(nèi)存胰坟。此時(shí)可以采用的方法就是將大量的數(shù)據(jù)按時(shí)間維度寫入多個(gè)csv文件,然后再根據(jù)需要的時(shí)間區(qū)間將多個(gè)csv文件打包下載即可泞辐,所以在這也講一下我如何實(shí)現(xiàn)文件打包笔横。
在php中,利用的是ZipArchive()
類咐吼,通過這個(gè)類的實(shí)例化來實(shí)現(xiàn)打包吹缔。
依然是感興趣的同學(xué)自行在php手冊上學(xué)習(xí)
代碼:
//獲取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();
這樣就實(shí)現(xiàn)了打包,其中$file_dir
變量是你要打包的文件的路徑數(shù)組锯茄,里面包含所有你想打包的文件路徑厢塘,$zip_file
變量是你想打包成zip文件的包的路徑+名稱。
有的同學(xué)在使用此方法時(shí)有時(shí)會不管用肌幽,以我的經(jīng)驗(yàn)晚碾,一般都是文件的路徑不正確,或者是沒有指定絕對路徑
注意:在$zip -> addFile()
方法中不要使用路徑變量拼接喂急,最好在使用該方法前就寫好路徑格嘁。使用了拼接不會報(bào)錯(cuò),但是依然會文件添加不進(jìn)去廊移,這里是一個(gè)大坑糕簿,我找了好久才發(fā)現(xiàn)。
最后:本人新手程序員狡孔,一起進(jìn)步6!苗膝!