CSV簡介
在開發后台管理系統的時候,幾乎無可避免的會遇到需要導入導出Excel表格的需求。csv也是表格的一種,其中文名為“逗號分隔符文件”。在Excel中打開如下圖左邊所示,在記事本打開如下圖右邊所示:
再看包含特殊字符的表格
與xls或xlsx 表格相類似,CSV文件也是用來表示二維表格。而不同的是:
1、CSV是一種純文本文件,任何編輯器都能打開並讀取它;xls(x)是專用的二進制文件,要用表格軟件才能正常打開,否則亂碼;
2、CSV的體積很小,比如上面的表格內容,csv只有幾十b;而xlsx有8k,老格式xls則有18k;
3、CSV的表達能力有限,只能表示二維數組(或一維);xls(x)等則有各種文件配置等等信息;可以參考JSON與XML的對比。
再看CSV的格式特點:
1、一般情況下,以逗號分隔一維數組(列),以換行分隔二維數組(行)。
2、當表格內容有,半角逗號、換行、空格、tab 等特殊字符時,外面使用雙引號括起來。
3、當表格內容有"雙引號時,轉換為兩個""雙引號,外面用雙引號括起來。
4、最后可以有空行,或者沒有空行。
PHP導出CSV
導出Excel幾乎是管理后台的標配功能。PHP能非常方便地設置HTTP header,控制輸出的形式,而二維數組轉換成CSV字符串也有成熟的函數庫處理,沒太大技術難點,直接貼源碼。要注意的是此方式導出的CSV最后一行數據有一個\n,即最后會有一個空行。
<?php /** * 導出為CSV格式 * @param string $filename 文件名,含后綴 * @param array $arr2D 表數據,二維數組 * @return 直接生成文件,用a標簽跳轉過來。本函數用了exit;以去除框架附加的debug信息 * */ function export_csv($filename, $arr2D){ header("Content-type:text/csv"); header("Content-Disposition:attachment;filename=".$filename); header('Cache-Control:must-revalidate,post-check=0,pre-check=0'); header('Expires:0'); header('Pragma:public'); $fp = fopen('php://output', 'w');//取自PHP官網的評論,直接輸出文本 foreach ($arr2D as $lines) { fputcsv($fp, $lines); } fclose($fp); exit; }
稍微說明一下:
1、HTTP header設置為 text/csv 則表示此文件是CSV格式文件以給瀏覽器處理,調用此函數前不要有其它輸出;
2、參數 $filename 是你要設置的下載的文件名(如:test.csv),參數 $arr2D 是要生成CSV的二維數組,CSV只支持二維數組;
PHP導入CSV
PHP導入CSV也非常簡單,php也有內置函數幫助處理。值得留意的是,此函數會自動去掉CSV最后的換行符(空行)
中文編碼問題,我們通常網站和數據庫都是使用UTF8編碼,最常使用的JSON也是UTF8編碼。但是Excel等軟件是基於微軟生態的,在中國通常是以GBK(含GB2312)編碼,如果不進行轉換編碼的話,會導致后續的使用不便、JSON轉換數據出錯等等問題。而用戶的文件上傳也沒法保證是utf8的還是gbk的,所以都需要兼容,PHP有個內置方法 mb_convert_encoding 可以輕松做到此事。
UTF8的BOM頭問題,因為PHP默認沒有處理BOM頭,會導致帶BOM頭UTF8的csv文件解析首行出BUG,所以要先處理一下。
<?php $filename = $_FILES['cvsfile']['tmp_name'];//這里的csvfile對應前端表單中的 input name="csvfile" $out = csv2arr($filename); //var_dump($out); //自己對數據進行處理,一般是反饋給前端讓用戶確認信息是否正確 function csv2arr($filename){ //去除BOM頭 $data = file_get_contents($filename); preg_replace("/^\xEF\xBB\xBF/", '', $data);// ltrim($data, "\xEF\xBB\xBF"); file_put_contents($filename, $data); $out = []; $handle = fopen($filename, 'r'); $n = 0; while ($data = fgetcsv($handle)){ $num = count($data); for ($i = 0; $i < $num; $i++){ //ANSI格式文本解析會出現亂碼,然后導致后續JSON轉換失敗 //注意編碼列表的順序,GBK 或 GB18030 放在utf8前面會導致utf8文件中文轉換亂碼 $data[$i] = mb_convert_encoding($data[$i], 'utf-8', ['utf-8', 'GB18030', 'BIG-5']); $out[$n][$i] = $data[$i]; } $n++; } return $out; }
注意:php里面有內置完善的 fputcsv 和 fgetcsv 等函數,不要自己輕易盲目去實現csv格式的解析,里面有不少坑,下篇JS處理時會講解。
JS與PHP的互動
一個Excel表的導入流程是這樣的:
選擇表格 --> 解析表格 --> 填充數據到表單 --> 用戶檢查 --> 提交表單
其中“解析表格 --> 填充數據到表單”這一步有幾種方案:
一、上傳csv文件到服務器,解析並生成整個表單頁面給前端;
二、上傳csv文件到服務器,解析返回JSON,前端JS填充表單;
三、前端JS本地解析csv文件生成JSON,然后填充表單;
其中第三個方案要現代H5瀏覽器才能實現,而且比較復雜,具體會在下一篇文章再講。現在簡單介紹第二個方案。
<form action="Test/csv2json" method="post" > <input type="file" name="csvfile" /> </form> <input type="button" onclick="csv1()" value="后台轉換"/> <script src="jquery.js"></script> <script src="jquery.form.js"></script> <script> function csv1(){ $("input[name=csvfile]").parent("form").ajaxSubmit(function(res){ console.log( res ); //TODO:這里是根據業務需要做的處理 writeForm( res ); }); } </script>
<?php function csv2json(){ $filename = $_FILES['csvfile']['tmp_name']; $out = csv2arr($filename); $this->ajaxReturn($out); }
這里的交互應該比較容易看得懂,就是前端按鈕觸發Ajax提交文件表單,然后后端轉換,並以JSON格式返回。最復雜的還是如何根據JSON填寫表單,不同的業務有不同的邏輯,這才是考驗前端DOM操作功力的時候。