5/09/2018

網頁資料存成 CSV 需要解決的問題整理﹍亂碼 + JS 技巧 + Bootstrap 表格

網頁資料存成 CSV 需要解決的問題整理﹍亂碼 + JS 技巧 + Bootstrap 表格

A+
最近客戶需要在網頁上顯示大量的會員資料後,製作一個「匯出」按鈕,將表格所有資料匯出存成 CSV 檔,再用 EXCEL 之類的試算表軟體開啟。

整個流程要用的的技術其實沒有太高深,但是陷阱卻是很多,因此整理成一篇備存。

(圖片出處: pexels.com)


一、需要解決的難題


1. JS 如何儲存 CSV

一開始需要瞭解如何將資料存成 CSV 檔,概念可參考這個討論串「How to export JavaScript array info to csv (on client side)?」:

  • 在頁面製作一個超連結,而網址連結的字串可以設定成 CSV 這樣的型態
  • 把所有資料格式整理好,用逗點分隔開,塞進 href 的值即可
  • 使用者點擊連結後,就能存成 CSV 檔


2. 解決 UNICODE 亂碼問題

可惜這樣的 CSV 檔,用 EXCEL 開啟後,只要內容有中文(UNICODE)就會產生亂碼,原因可參考這篇「【Javascript】匯出 csv」。

利用這篇的技巧,在 CSV 檔開頭前幾個字元,塞入特定的編碼字串,可讓 CSV 檔成功在 EXCEL 顯示中文。


3. 資料太多時會被截斷

看完前面第一點的概念後,就知道存成 CSV 檔是靠網址傳值的概念。但客戶的會員資料起碼有幾萬筆,很快就超出瀏覽器網址字串限制的長度,所以無法成功儲存所有的會員資料為 CSV 檔。

這個討論串「Export HTML table to csv in google chrome browser」使用了不同的作法:

  • 原本的作法藉著網址傳遞字串資料,會受到網址字串的長度限制。
  • 新的作法將字串轉換為二進位資料(BLOB),依然用網址傳遞,但資料型態不同,就不會受到字串的長度限制。
  • BLOB 格式的資料大小依然會有上限,但存成 CSV 檔的資料型態,要超出限制是極度困難的事情,所以除非資料多到像影片檔那麼大,那麼可不必擔心此事。


4. 依然是 UNICODE 亂碼問題

解決資料長度問題,改用 BLOB 格式後,又要再度面臨亂碼問題。

找到這篇「使用 JavaScript 导出 CSV 文件」,一樣在資料開始處加入特殊編碼字串,終於一切都正常了。



二、表格範例 + 匯出按鈕



匯出 WFU BLOG 會員資料

權限 序號 暱稱 性別 註冊日期
加值會員 W00001 Wayne Fu 男生 2014/9/12
一般會員 W00002 Che 女生 2014/9/17
一般會員 W00003 Ken 男生 2014/9/17
一般會員 W00004 Sun 男生 2014/9/17
一般會員 W00005 Liu 男生 2014/9/17
一般會員 W00006 Don 男生 2014/9/18
一般會員 W00007 Cha 女生 2014/9/18
一般會員 W00008 Tun 女生 2014/9/18
一般會員 W00009 男生 2014/9/18
一般會員 W00010 HY 男生 2014/9/18
一般會員 W00011 女生 2014/9/18
一般會員 W00012 Kat 女生 2014/9/19
一般會員 W00013 Mar 男生 2014/9/19
一般會員 W00014 Wan 男生 2014/9/20
一般會員 W00015 Bas 女生 2014/9/20
一般會員 W00016 男生 2014/9/23
一般會員 W00017 女生 2014/9/23
一般會員 W00018 Li 男生 2014/10/2
一般會員 W00019 女生 2014/10/2
一般會員 W00020 男生 2014/10/3




三、範例程式碼


以上效果的範例程式碼如下:

<script src='//ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js'></script>
<link href='//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css' rel='stylesheet'></link>

<a id="exportBtn" class="btn btn-success" target="_blank" download="WFUBLOG-會員資料.csv">匯出 WFU BLOG 會員資料</a>
<br/><br/>
<style>
#member_list {white-space: nowrap;}
#member_list thead{background: #5893c8;color:#fff;}
</style>
<div id="member_list" class="table-responsive">
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>權限</th>
<th>序號</th>
<th>暱稱</th>
<th>性別</th>
<th>註冊日期</th>
</tr>
</thead>
<tbody>
<tr>
<td>加值會員</td>
<td>W00001</td>
<td>Wayne Fu</td>
<td>男生</td>
<td>2014/9/12</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00002</td>
<td>Che</td>
<td>女生</td>
<td>2014/9/17</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00003</td>
<td>Ken</td>
<td>男生</td>
<td>2014/9/17</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00004</td>
<td>Sun</td>
<td>男生</td>
<td>2014/9/17</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00005</td>
<td>Liu</td>
<td>男生</td>
<td>2014/9/17</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00006</td>
<td>Don</td>
<td>男生</td>
<td>2014/9/18</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00007</td>
<td>Cha</td>
<td>女生</td>
<td>2014/9/18</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00008</td>
<td>Tun</td>
<td>女生</td>
<td>2014/9/18</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00009</td>
<td>陳</td>
<td>男生</td>
<td>2014/9/18</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00010</td>
<td>HY</td>
<td>男生</td>
<td>2014/9/18</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00011</td>
<td>美</td>
<td>女生</td>
<td>2014/9/18</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00012</td>
<td>Kat</td>
<td>女生</td>
<td>2014/9/19</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00013</td>
<td>Mar</td>
<td>男生</td>
<td>2014/9/19</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00014</td>
<td>Wan</td>
<td>男生</td>
<td>2014/9/20</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00015</td>
<td>Bas</td>
<td>女生</td>
<td>2014/9/20</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00016</td>
<td>莊</td>
<td>男生</td>
<td>2014/9/23</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00017</td>
<td>詹</td>
<td>女生</td>
<td>2014/9/23</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00018</td>
<td>Li</td>
<td>男生</td>
<td>2014/10/2</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00019</td>
<td>陳</td>
<td>女生</td>
<td>2014/10/2</td>
</tr>
<tr>
<td>一般會員</td>
<td>W00020</td>
<td>淵</td>
<td>男生</td>
<td>2014/10/3</td>
</tr>
</tbody>
</table>
</div>
<script>
// 點擊匯出
$("#exportBtn").click(function() {
var csvList = [],
titleList = [],
memberContent = "",
csvContent;

// 取得標題
$("#member_list th").each(function() {
titleList.push(this.innerHTML);
});
csvList.push(titleList);

// 取得所有資料
$("#member_list tbody > tr").each(function() {
var regList = [];
$(this).children("td").each(function() {
regList.push(this.innerHTML);
});
csvList.push(regList);
});

// 產生 csv 內容
csvList.forEach(function(rowArray) {
var row = rowArray.join(",");
memberContent += row + "\r\n";
});

// 產生 csv Blob
csvContent = URL.createObjectURL(new Blob(["\uFEFF" + memberContent], {
type: 'text/csv;charset=utf-8;'
}));

// 產生 csv 連結
this.href = csvContent;
});
</script>


  • 前 2 行綠字可參考「引用 jQuery 的注意事項」,檢查範本是否已安裝過 jQuery、Bootstrap,如果已經安裝過請刪除,以免重複安裝。
  • 藍字可修改下載的檔名
  • 如果熟悉 JS,可參考註釋內容自行修改


更多「網頁技巧」相關文章:
如這篇文章對你有幫助,歡迎「分享」或給我個讚:

2 則留言:

  1. 那反過來用 JS 上傳 csv 成為 table 的方法呢?
    管理

    回覆刪除
    回覆
    1. 先用 Html5 File Api 取得上傳內容 https://gist.github.com/tanvir002700/c65f5c6608c16540a72688f011cf2485

      接著需要操作 DOM 的技巧,把這些內容,動態放進 table 裡面囉~

      刪除

張貼留言注意事項:

◎ 勾選「通知我」可收到後續回覆的mail!
◎ 請在相關文章留言,與文章無關的主題請至「Blogger 中文論壇」。
◎ 提問若無法提供足夠的資訊供判斷,可能會被無視。建議先參考這篇「Blogger 提問技巧及注意事項」。
◎ CSS 相關問題非免費諮詢,建議使用「Chrome 開發人員工具」尋找答案。
◎ 手機版相關問題請參考「Blogger 行動版範本的特質」→「三、行動版範本不一定能執行網頁版工具」。
◎ 非官方範本問題、或貴站為商業網站,請參考「本站諮詢頁面」→「1. 諮詢服務」
◎ 若留言要輸入語法,"<"、">"這兩個符號請用其他符號代替,否則語法會消失!
◎ 為了過濾垃圾留言,所有留言不會即時發佈,請稍待片刻。
◎ 本站「已關閉自刪留言功能」。

TOP