我初學 javascript 時, 萬萬沒想到最困擾的竟會是 「讀檔」 這個單純/重要/基本的工作, 更沒想到讀本地的檔案竟然比讀遠方的檔案更困難! 現在摸出門路了, 快用 jquery 所提供的 $.get()) 跟 promise 機制寫一個 一次讀多個檔案的簡單範例 readfile-jq.js 跟 readfile.html, 幫你省下當初我不斷撞牆的辛酸路。 因為程式太短了, 就再加碼順便示範如何用 DataTables 跟 jqTree 把 .csv 跟 .json 兩類資料結構清楚地呈現在 html 頁面上, 以便 (訪客) 不必開 console 就能略微 (幫你) 除錯。
一、 從網址讀檔
先在 html 裡面引用 jquery:
<script src="https://code.jquery.com/jquery-1.12.3.min.js"></script>
然後在程式碼裡面就可以用 $.get() 讀取網路上的某個檔案。 不過我們也很常需要一次讀完好幾個檔案再開始處理。 根據 這個問答, 採用 $.when().done() 就可以一次讀好幾個檔, 不需要繁雜的 nested callback:
clothes = $.get(網址一); gift = $.get(網址二); $.when(clothes, gift).done(init);
那兩個網址是我其他程式的範例資料檔, 都是高雄鹽埕區的商家地理資訊。 一個是服飾店清單, .csv 格式; 另一個是禮品店清單, .geojson 格式。 傳回來的是一個 promise。 你可以搜尋 「promise 機制」 進一步研究; 不過我急著只想用 .when 跟 .done 把資料讀進來就好; 至於 promise 是什麼... 請允許我 「保證」 以後再找時間再研究吧 :-)
在 callback (也就是 init() ) 裡面, 所接收到的兩個參數就對應到當初傳入 when 的兩個參數。 每個參數是一個陣列, 第 0 個元素就是讀進來的檔案內容, 存成一個很長的字串。 所以你可以這樣確認讀檔成功:
function init(clothes, gift) { console.log('clothes: ', clothes[0], ' \ngift', gift[0]); }
如果遠方的伺服器不讓你的 javascript 讀資料, 可能需要請站長開放 CORS。
如果你的資料檔採用 json 格式儲存, jQuery 還提供一個更方便的 $.getJSON() 函數。 它會自動幫你把讀進來的一長條字串解析成 javascript 的資料結構。 也就是說, 這一段:
gift = $.get(網址二); console.log(gift); // 以一長條字串印出 console.log(JSON.parse(gift)); // 以物件格式印出
跟這一段:
gift = $.getJSON(網址二); console.log(JSON.stringify(gift)); // 以一長條字串印出 console.log(gift); // 以物件格式印出
效果相等。
二、 讀本地檔
或者, 檔案也不一定要放在雲端 -- $.get() 跟 $.getJSON() 也可以讀本機的檔案。 前提是: 你必須用網址的方式開啟網頁: http://localhost/...html 也就是說你需要自架 apache2 或 nginx。 如果你想用 「檔案=>開啟」 的方式開啟網頁, 也就是網址列顯示 file:///...html 那就比較麻煩了。
如果你的瀏覽器是 chromium, 那麼啟動時必須在命令列上這樣下才可以讀本地檔:
chromium-browser --allow-file-access-from-files
。
詳見
chrome 的禁讀令。
如果你的瀏覽器是 firefox, 那麼你必須
關閉 strict_origin_policy,
而且只能用 jQuery.getJSON。
在這個情況下, jQuery 的 .get .when .done 的 callback 都無效。
但是 .get() 還是會去讀檔案, 所以只要加上
window.setTimeout() 等待一會兒, 資料還是會出現。
那麼, 「使用者到底是用 http:// 還是用 file:/// 開啟你的頁面?」
這在你的程式裡該如何測試呢? 可以用
if (window.location.protocol == 'file:') { ... }
詳見
這個問答。
總之, 先前我換了很多搜尋關鍵詞, 但是很奇怪地, 新手詢問讀取本地檔案的問題 總是得不到簡單清楚的答覆。 更別提想要一次讀多個檔案, 還必須用頭昏腦漲的巢狀 callbacks。 如果不是 jQuery 或 d3 這類函式庫出面解救, 我對 javascript 早就棄鍵投降了。 為了表達抗議, 我開了一個 github 專案 :-) javascriptCanReadLocalFiles , 你可以到那裡抄範例程式碼。
只想要讀檔案的同學, 到這裡就可以下課了 :-)
但是如果想把資料印出來呢?
開發者自己當然可以用 console.log()
來檢視; 但若想把大量資料印給網頁訪客看呢?
其他程式語言沒有這個區別;
javascript 就比較辛苦一點了。
還好有 jQuery 跟很多的 jQuery 外掛。
三、 用 DataTables 在網頁上顯示 (例如從 .csv 檔讀進來的) 表格
用 DataTables 外掛可以讓你的 html table 變得更清楚漂亮且具互動性, 很適合拿來呈現二維表格類型的資料, 例如 .csv 檔讀進來的東西。 在 html 裡面除了要引用 css 跟程式碼:
<link type="text/css" rel= "stylesheet" href="https://cdn.datatables.net/1.10.12/css/jquery.dataTables.min.css"/> <script src="https://cdn.datatables.net/1.10.12/js/jquery.dataTables.min.js"></script>
還要預留一個 table:
<table id="table_display" class="display"> </table>
然後在程式裡呼叫 .DataTable() 建立表格:
$('#table_display').DataTable({ ordering: false, // 國名跟都市名稱沒什麼好排序的啊! columns: [ { title: '國家' }, { title: '首都' } ], data: [ ['臺灣', '臺北'], ['越南', '河內'], ['印尼', '雅加達'] ] });
以我們讀入的 .csv 資料而言, 要先做以下前置處理:
- 用
.split('\n')
把資料拆成一列一列。 - 用
.shift()
抓出第一列當表頭 (整修一下當做 columns 欄) - 用
.forEach()
跟.split(',')
把剩下的內容拆成一個二維陣列 (當做 data 欄)。
DataTable 提供的使用者互動功能包含大量資料分頁顯示、 排序、 搜尋等等。 也提供多種佈景主題。 更進一步學習時, 推薦的閱讀順序: 從 Examples index 著手最簡單。 先想一下你的資料表格來源到底是靜態網頁、 ajax 遠端讀取、 javascript 程式碼、 還是來自本身伺服器端? 從 「Data sources」 那邊挑一個符合你需求的範例來抄。 再看看這個頁面的其他範例有沒有你需要學的功能。
四、 用 jqTree 在網頁上顯示樹狀資料結構 (例如 json)
jqTree 外掛可以拿來呈現樹狀的資料結構, 例如從 .json 檔讀進來的東西。 在 html 裡面除了要引用 css 跟程式碼:
<link type="text/css" rel= "stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqtree/1.3.3/jqtree.min.css"/> <script src="https://cdnjs.cloudflare.com/ajax/libs/jqtree/1.3.3/tree.jquery.min.js"></script>
還要預留一個 div:
<div id="tree_display"> </table>
然後在程式裡呼叫 .tree() 建立表格:
$('#tree_display').tree({ data: ['root'] });
建議先剪貼 jqTree 首頁的範例來測試。 基本上就是傳一個陣列給 data 欄, 裡面每個元素是一個 hash, 有 name 跟 children 兩欄。 children 裡面又是另一層的陣列...
我寫的 json2jqTree() 函數可以把一個 json 結構轉成 jqTree 所需要的格式。 這個函數很適合拿來當做遞迴練習作業; 不會遞迴的同學直接剪貼帶走, 搭配 jqTree 也很實用。
jqTree 畫出來的樹狀結構可以收縮/展開, 讓訪客可以自行決定如何善用螢幕空間。
加了一節 「讀本地檔」, 還開了一個 github 專案表達抗議 :-)
回覆刪除太感謝了
回覆刪除callback 真的是弄不好
希望以後能搞懂