一個 CSV 檔案是一個以表格格式儲存資料的純文字檔案。在大多數情況下,CSV 檔案使用逗號 (,) 作為分隔符,因此得名 CSV (逗號分隔值)。它’被用於需要考慮資料相容性的情況,因為 CSV 可以用任何文字編輯器、試算表應用程式和其他專業工具打開。事實上,許多程式語言都提供了對 CSV 的內建支援。
在本指南中,我們將學習如何在範例 Node.js 應用程式中使用 CSV。
在 Node.js 中使用 CSV
Node.js 是一個開源且跨平台的 JavaScript 執行環境。它已成為支援網際網路上眾多網路服務最受歡迎的後端之一。甚至像 Netflix 和 Uber 這樣的大公司也使用 Node.js 來支援他們的服務。
Node.js 也有許多模組可用於部署,以在專案中增加額外功能。說到 CSV,有許多模組可以使用,例如:node-csv, fast-csv、以及 papaparse 等。
正如指南標題所示,我們將使用 node-csv 來使用 Node.js 串流讀取 CSV 檔案。我們還將示範如何處理解析後的資料,例如將資料傳輸到 SQLite 資料庫中。
先決條件
-
要執行本指南中示範的步驟,您將需要以下組件:
-
一個配置妥當的 Linux 系統。深入了解 在 CloudSigma 上安裝和設定 Ubuntu 雲端伺服器.
-
使用具有 sudo 權限的非 root 使用者。請參閱 使用 sudoers 管理 sudo 權限.
-
適合的文字編輯器,例如:Brackets, VS Code, Sublime Text, Vim/NeoVim 等。
-
其他軟體:
-
Node.js LTS
-
SQLite
-
步驟 1 – 安裝必要軟體
在本指南中,我建立了一個執行 Ubuntu 22.04 LTS 的輕量級伺服器(透過 SSH 連線):

現在,我們將在上面安裝 Node.js 和 SQLite。
-
安裝 Node.js LTS
Node.js 可以直接從 Ubuntu 官方套件庫中取得。然而,它’並不是最新版本。這就是為什麼我們將依賴第三方套件庫(Nodesource)來獲取最新的 Node.js 套件。
新增 Node.js LTS 的套件庫:
|
1 |
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - |

現在,安裝 Node.js LTS:
|
1 |
sudo apt install nodejs -y |
-
安裝 SQLite
我們將直接從 Ubuntu 套件庫安裝 SQLite。執行以下命令:
|
1 |
sudo apt install sqlite3 -y |

步驟 2 – 專案目錄設定
在本節中,我們將為專案準備一個專用目錄。它將存放所有專案檔案以及額外的模組。
建立一個新目錄:
|
1 |
mkdir -pv csv_practice |
進入該目錄:
|
1 |
cd csv_practice/ |
接下來,執行以下命令將該目錄宣告為 npm 專案:
|
1 |
npm init -y |

專案資料夾初始化完成後,我們就可以開始安裝必要的套件和模組。首先,我們將安裝 node-csv:
|
1 |
npm install csv |

node-csv 模組實際上是其他幾個模組的集合:csv-generate, csv-parse(解析 CSV 檔案)、csv-stringify(將資料寫入 CSV)以及 stream-transform.
接下來,我們需要用於與 SQLite 通訊的模組。以下命令將安裝 node-sqlite3 模組:
|
1 |
npm install sqlite3 |

我們專案所需的組件是一個 CSV 檔案。為了示範目的,我們將使用紐西蘭移民 CSV 檔案:
|
1 |
wget https://www.stats.govt.nz/assets/Uploads/International-migration/International-migration-September-2021-Infoshare-tables/Download-data/international-migration-September-2021-estimated-migration-by-age-and-sex-csv.csv -O migration_data.csv |

我們來快速查看一下檔案的內容:
|
1 |
cat migration_data.csv | less |

這裡,
-
第一行描述了欄位名稱。
-
後續的行則包含這些欄位的值。
-
每一行都由換行符號 (\n) 分隔。
-
每個資料點都由逗號 (,) 分隔。
然而,CSV 並不限於使用逗號作為分隔符。其他常見的分隔符包括冒號 (:)、分號 (;) 和定位鍵 (\td)。
步驟 3 – 讀取 CSV
在本節中,我們將示範實作一個從 CSV 檔案讀取並解析資料的範例程式。
建立一個新的 JavaScript 檔案:
|
1 |
touch read_csv.js |
在您喜愛的文字編輯器中開啟該檔案:
|
1 |
nano read_csv.js |
首先,我們將匯入 fs 與 csv-parse 模組:
|
1 2 |
const fs = require("fs"); const { parse } = require("csv-parse"); |
這裡,
-
首先, fs 變數被指派了 fs 物件,該物件在匯入模組時會傳回 Node.js 的 require() 方法。
-
接下來,從 require() 方法傳回的物件中,使用 解構語法.
接下來,我們將加入讀取 CSV 檔案的程式碼:
|
1 2 3 4 5 |
fs.createReadStream("./migration_data.csv") .pipe(parse({ delimiter: ",", from_line: 2 })) .on("data", function (row) { console.log(row); }) |
這裡,
-
我們正在呼叫 createReadStream() (來自 fs 模組),並將我們想要讀取的 CSV 檔案作為參數傳遞。然後,它透過將較大的檔案拆分為較小的區塊來建立一個可讀取串流(readable stream)。
-
建立串流後, pipe() 方法會將串流資料的區塊轉發到另一個串流。這個新串流是在呼叫 parse() 方法(來自 csv-模組.
-
這個 csv-模組 部署了一個可讀寫的轉換串流(transform stream),它接收資料區塊並將其轉換為另一種形式。
-
這個 parse() 方法接受具有屬性的物件。該物件會進一步處理已解析的資料。在這裡,該物件具有以下屬性:
-
delimiter:用於分隔值的分隔字元。在我們的目標 CSV 中,它是逗號 (,)。
-
from_line:解析器開始解析的行數。給定值為 2 時,解析器將跳過第 1 行並從第 2 行開始。透過這種安排,我們可以避免將欄位名稱整合到解析後的資料中。
-
接下來,我們將使用 Node.js 的 on() 方法來附加一個串流事件:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
fs.createReadStream("./migration_data.csv") .pipe(parse({ delimiter: ",", from_line: 2 })) .on("data", function (row) { console.log(row); }) .on("end", function () { console.log("finished"); }) .on("error", function (error) { console.log(error.message); }); |
這裡,
-
在觸發特定事件時,串流事件允許方法取用資料區塊。
-
當由 parse() 方法解析的資料準備好被取用時,它會觸發 data 事件。
-
為了存取資料,我們向 on() 方法傳遞一個接受 row 參數的回呼函式。
-
row 參數是陣列形式的資料區塊(解析後的結果)。
-
最後,使用 console.log().
將資料記錄在主控台中。為了完成程式,我們將加入額外的串流事件來處理錯誤,並在 CSV 檔案中的所有資料都被取用完畢時列印成功訊息。請如下更新程式碼:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
fs.createReadStream("./migration_data.csv") .pipe(parse({ delimiter: ",", from_line: 2 })) .on("data", function (row) { console.log(row); }) .on("end", function () { console.log("finished"); }) .on("error", function (error) { console.log(error.message); }); |
這裡,
-
當 CSV 檔案中的所有資料都被消耗完時,會觸發 end 事件。這會導致呼叫 console.log() 方法,該方法會列印成功訊息。
-
在解析 CSV 資料時如果遇到錯誤,會觸發 error 事件。這會導致呼叫 console.log() 方法,該方法會列印錯誤訊息。
最終的程式碼應該像這樣:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const fs = require("fs"); const { parse } = require("csv-parse"); fs.createReadStream("./migration_data.csv") .pipe(parse({ delimiter: ",", from_line: 2 })) .on("data", function (row) { console.log(row); }) .on("end", function () { console.log("finished"); }) .on("error", function (error) { console.log(error.message); }); |

儲存檔案並關閉編輯器。我們現在準備好執行程式了。使用 Node.js 執行它:
|
1 |
node read_csv.js |
輸出應該看起來像這樣:

請注意,資料會被消耗、轉換並列印在主控台上。因為這是一個持續的過程,所以看起來會像是正在下載資料,而不是一次列印出所有輸出。
步驟 4 – 將 CSV 資料傳輸到資料庫
到目前為止,我們已經學習了如何使用 node-csv 解析 CSV 檔案。本節將示範如何將解析後的資料傳輸到資料庫 (SQLite) 中。
建立一個新的 JavaScript 檔案以與資料庫進行互動:
|
1 |
touch csv-to-sqlite3.js |
現在,在文字編輯器中開啟該檔案:
|
1 |
nano csv-to-sqlite3.js |
![]()
我們將使用以下程式碼開始我們的程式:
|
1 2 3 |
const fs = require("fs"); const sqlite3 = require("sqlite3").verbose(); const filepath = "./population.db"; |

這裡,
-
在第一行中,我們正在匯入 fs 模組。
-
在第三行中,變數 filepath 包含 SQLite 資料庫的路徑。
-
此時,資料庫還不存在。然而,當與 node-sqlite3.
搭配工作時,這將是必需的。接下來,加入以下幾行以建立與 SQLite 資料庫的連線:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function connectToDatabase() { if (fs.existsSync(filepath)) { return new sqlite3.Database(filepath); } else { const db = new sqlite3.Database(filepath, (error) => { if (error) { return console.error(error.message); } console.log("Connected to the database successfully"); }); return db; } } |

在這裡,
-
此方法 connectoToDatabase() 建立與資料庫的連接。
-
在 connectToDatabase() 中,我們正在呼叫 existsSync() 方法,該方法來自 if 語句中的 fs 模組。if 語句會檢查指定位置中是否存在該資料庫。
-
如果條件評估為 true,那麼 Database() 類別,屬於 node-sqlite3 模組。一旦建立連接,該函式就會返回一個物件並結束。
-
如果條件評估為 false (資料庫不存在),則執行將跳轉到 else 區塊。在該區塊中, Database() 類別將使用兩個引數進行初始化:資料庫檔案的路徑和一個回呼函式。
-
基本上,如果資料庫不存在,系統將會建立它。然而,如果在建立過程中發生任何錯誤,它將會設定 error 物件並列印錯誤訊息。
接下來,我們將引入程式碼,以便在資料庫不存在時建立資料表:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
const fs = require("fs"); const sqlite3 = require("sqlite3").verbose(); const filepath = "./population.db"; function connectToDatabase() { if (fs.existsSync(filepath)) { return new sqlite3.Database(filepath); } else { const db = new sqlite3.Database(filepath, (error) => { if (error) { return console.error(error.message); } createTable(db); console.log("Connected to the database successfully"); }); return db; } } function createTable(db) { db.exec(` CREATE TABLE migration ( year_month VARCHAR(10), month_of_release VARCHAR(10), passenger_type VARCHAR(50), direction VARCHAR(20), sex VARCHAR(10), age VARCHAR(50), estimate INT ) `); } module.exports = connectToDatabase(); |

在這裡,
-
此 connectToDatabase() 呼叫了 createTable() 函式,該函式接受儲存在 db 中的物件作為引數。
-
在 connectToDatabase() 之外,我們定義了 createTable() 方法,該方法接受連接物件 db 作為參數。
-
此 exec() 方法(在 db 上)接受一個 SQL 語句作為引數。在此 SQL 語句中,我們定義了建立一個名為 migration 的資料表,其中包含 7 個欄位,每個欄位對應於 migration_data.csv 檔案中的欄位標題。
-
最後,我們正在呼叫 connectToDatabase() 方法並匯出它傳回的連線物件,以便我們可以在其他檔案中使用它。
儲存檔案並關閉編輯器。
接下來,我們將建立另一個程式來將解析後的資料插入資料庫:
|
1 |
nano insert_data.js |
在 中輸入以下程式碼:insert_data.js:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
const fs = require("fs"); const { parse } = require("csv-parse"); const db = require("./csv-to-sqlite3"); fs.createReadStream("./migration_data.csv") .pipe(parse({ delimiter: ",", from_line: 2 })) .on("data", function (row) { db.serialize(function () { db.run( `INSERT INTO migration VALUES (?, ?, ? , ?, ?, ?, ?)`, [row[0], row[1], row[2], row[3], row[4], row[5], row[6]], function (error) { if (error) { return console.log(error.message); } console.log(`Row inserted, ID: ${this.lastID}`); } ); }); }); |

在這裡,
-
我們正在將從 csv-to-sqlite3.js 取得的連線物件儲存在變數 db.
-
在 data 事件回呼(附加到 fs 模組串流)中,我們正在呼叫 serialize() 方法(在連線物件上)。它可確保一個 SQL 陳述式在下一個陳述式開始執行之前完成執行,從而防止資料庫競爭條件(系統同時執行競爭操作)。
-
此 serialize() 接受三個參數:
-
第一個參數是 SQL 陳述式。
-
第二個參數是一個陣列。
-
第三個參數是一個回呼,當資料成功或失敗插入資料庫時會執行。
-
我們已準備好執行程式。請執行 insert_data.js(使用 Node.js):
|
1 |
node insert_data.js |

根據系統’的效能,該程序可能需要一些時間才能完成。然而,完成後,輸出應該看起來像這樣:
步驟 5 – 將資料寫入 CSV
在上一節之後,我們得到了一個資料庫,其中包含我們從 migration_data.csv 解析的所有記錄。在本節中,我們將從資料庫中讀取資料並將其寫入一個獨立的 CSV 檔案中。
建立一個新的 JavaScript 檔案來儲存程式:
|
1 |
nano write_csv.js |
首先,加入以下幾行以匯入 fs 以及 csv-stringify ,以及來自 csv-to-sqlite3.js:
|
1 2 3 |
const fs = require("fs"); const { stringify } = require("csv-stringify"); const db = require("./csv-to-sqlite3"); |
接下來,我們將添加一個變數,其中包含要寫入的 CSV 檔案名稱以及一個可寫入串流:
|
1 2 3 4 5 6 7 8 9 10 11 |
const filename = "saved_from_db.csv"; const writableStream = fs.createWriteStream(filename); const columns = [ "year_month", "month_of_release", "passenger_type", "direction", "sex", "age", "estimate", ]; |

這裡,
-
此 createWriteStream() 方法將要寫入的檔案名稱作為參數。我們將該檔案命名為 saved_from_db.csv.
-
此 column 變數儲存了一個包含 CSV 資料所有標頭名稱的陣列。
接下來,添加以下幾行程式碼以從資料庫讀取資料並將其寫入 saved_from_db.csv:
|
1 2 3 4 5 6 7 8 9 10 11 |
const stringifier = stringify({ header: true, columns: columns }); db.each(`select * from migration`, (error, row) => { if (error) { return console.log(error.message); } stringifier.write(row); }); stringifier.pipe(writableStream); console.log("finished writing to CSV"); |

這裡,
-
我們正在呼叫 stringify() 方法,並將一個物件作為參數。這會產生一個轉換串流,將資料從物件格式轉換為 CSV 格式。傳遞給 stringify() 具有兩個屬性:
-
header:接受一個布林值。如果值為 true,則會生成標頭。
-
columns:接受一個包含欄位名稱的陣列,如果 header 為 true.
-
-
此 each() 方法來自 csv-to-sqlite3 連線物件,並使用兩個參數進行呼叫:SQL 語句(從資料庫讀取資料)和回呼函式(處理成功/錯誤)。
-
在每次迭代 each(), pipe()(來自 stringifier 串流)開始將資料分塊傳送到可寫入串流 writableStream。然後將每塊資料寫入 saved_from_db.csv.
-
當所有資料都寫入 CSV 檔案時,主控台螢幕上會印出成功訊息。
最終的程式碼應該像這樣:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
const fs = require("fs"); const { stringify } = require("csv-stringify"); const db = require("./csv-to-sqlite3"); const filename = "saved_from_db.csv"; const writableStream = fs.createWriteStream(filename); const columns = [ "year_month", "month_of_release", "passenger_type", "direction", "sex", "age", "estimate", ]; const stringifier = stringify({ header: true, columns: columns }); db.each(`select * from migration`, (error, row) => { if (error) { return console.log(error.message); } stringifier.write(row); }); stringifier.pipe(writableStream); console.log("finished writing to CSV"); |

儲存檔案並關閉編輯器。我們現在可以使用 Node.js 執行該程式:
|
1 |
node write_csv.js |
要確認資料是否已成功匯出,請檢查 saved_from_db.csv:
|
1 |
cat saved_from_db.csv | less |

結語
在本指南中,我們示範了如何在 Node.js 中使用 node-csv 和 node-sqlite3 模組處理 CSV 檔案。我們建立了多個程式來完成各種任務,例如:解析 CSV 中的資料、將資料推送到 SQLite 資料庫,以及將資料寫入新的 CSV 檔案。
本指南僅展示了 node-csv 模組功能的一小部分。欲了解其所有功能的更多資訊,請參閱 CSV Project。欲了解更多關於 node-sqlite3 的資訊,請查看 GitHub 上的官方文件。另一個值得一提的模組是 event-stream,可用於簡化串流的操作。
有興趣進一步擴展您的 Node.js 專案嗎?以下是一些您應該參考的 Node.js 教學課程:
祝您開發愉快!
留言
目前尚無留言。成為第一個留言的人吧。