iT邦幫忙

2025 iThome 鐵人賽

DAY 5
0

前言

在接觸 Node.js 的 fs (File System)模組之前,對電腦中檔案操作的理解,就只停留在右鍵新增,右鍵刪除,所以在發現到可以借由程式碼去操控檔案滿有趣的,也理解了一些更底層的原理。

基本用法

fs 模組提供了與檔案系統互動的 API,讓我們能夠讀取、寫入、修改和刪除檔案,以及操作目錄。這是 Node.js 的核心模組之一,不需要額外安裝即可使用。

const fs = require('node:fs');
const fsPromises = require('node:fs/promises');

// 使用 ES 模組語法
import * as fs from 'node:fs';
import * as fsPromises from 'node:fs/promises';
// 或者
import { readFile, writeFile } from 'node:fs';
import { readFile as readFileAsync, writeFile as writeFileAsync } from 'node:fs/promises';

同步與非同步 API

fs 模組提供了同步和非同步兩種版本的 API:

  • 同步版本:會阻塞程式執行直到操作完成
  • 非同步版本:不阻塞程式執行,通過回調函數、Promise 或 async/await 處理結果

非同步範例 (使用回調)

const fs = require('node:fs');

// 非同步讀取檔案
fs.readFile('example.txt', 'utf8', (err, data) => {
  if (err) {
    console.error('讀取檔案時發生錯誤:', err);
    return;
  }
  console.log('檔案內容:', data);
});

console.log('這行會先執行,不會等待檔案讀取完成');

同步範例

const fs = require('node:fs');

try {
  // 同步讀取檔案
  const data = fs.readFileSync('example.txt', 'utf8');
  console.log('檔案內容:', data);
} catch (err) {
  console.error('讀取檔案時發生錯誤:', err);
}

console.log('這行會等待檔案讀取完成後才執行');

使用 Promise API (node:fs/promises)

const fsPromises = require('node:fs/promises');

// 或使用 ES 模組語法
// import * as fsPromises from 'node:fs/promises';

async function readMyFile() {
  try {
    const data = await fsPromises.readFile('example.txt', 'utf8');
    console.log('檔案內容:', data);
  } catch (err) {
    console.error('讀取檔案時發生錯誤:', err);
  }
}

readMyFile();

常用檔案操作

讀取檔案

const fs = require('node:fs');
const fsPromises = require('node:fs/promises');

// 回調方式
fs.readFile('example.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log(data);
});

// Promise 方式
fsPromises.readFile('example.txt', 'utf8')
  .then(data => console.log(data))
  .catch(err => console.error(err));

// 同步方式
const data = fs.readFileSync('example.txt', 'utf8');

寫入檔案

const fs = require('node:fs');
const fsPromises = require('node:fs/promises');

// 回調方式
fs.writeFile('output.txt', '這是新內容', 'utf8', (err) => {
  if (err) throw err;
  console.log('檔案已被保存');
});

// Promise 方式
fsPromises.writeFile('output.txt', '這是新內容', 'utf8')
  .then(() => console.log('檔案已被保存'))
  .catch(err => console.error(err));

// 同步方式
fs.writeFileSync('output.txt', '這是新內容', 'utf8');

附加內容到檔案

const fs = require('node:fs');
const fsPromises = require('node:fs/promises');

// 回調方式
fs.appendFile('log.txt', '新的日誌條目\n', (err) => {
  if (err) throw err;
  console.log('數據已附加到檔案');
});

// Promise 方式
fsPromises.appendFile('log.txt', '新的日誌條目\n')
  .then(() => console.log('數據已附加到檔案'))
  .catch(err => console.error(err));

// 同步方式
fs.appendFileSync('log.txt', '新的日誌條目\n');

檢查檔案存在

const fs = require('node:fs');
const fsPromises = require('node:fs/promises');

// 推薦方法:使用 access 檢查檔案存在性
fs.access('example.txt', fs.constants.F_OK, (err) => {
  console.log(err ? '檔案不存在' : '檔案存在');
});

// Promise 方式
fsPromises.access('example.txt', fs.constants.F_OK)
  .then(() => console.log('檔案存在'))
  .catch(() => console.log('檔案不存在'));

// 同步方式
try {
  fs.accessSync('example.txt', fs.constants.F_OK);
  console.log('檔案存在');
} catch (err) {
  console.log('檔案不存在');
}

刪除檔案

const fs = require('node:fs');
const fsPromises = require('node:fs/promises');

// 回調方式
fs.unlink('unnecessary.txt', (err) => {
  if (err) throw err;
  console.log('檔案已刪除');
});

// Promise 方式
fsPromises.unlink('unnecessary.txt')
  .then(() => console.log('檔案已刪除'))
  .catch(err => console.error(err));

// 同步方式
fs.unlinkSync('unnecessary.txt');

目錄操作

創建目錄

const fs = require('node:fs');
const fsPromises = require('node:fs/promises');

// 回調方式
fs.mkdir('newDir', { recursive: true }, (err) => {
  if (err) throw err;
  console.log('目錄已創建');
});

// Promise 方式
fsPromises.mkdir('newDir', { recursive: true })
  .then(() => console.log('目錄已創建'))
  .catch(err => console.error(err));

// 同步方式
fs.mkdirSync('newDir', { recursive: true });

讀取目錄內容

const fs = require('node:fs');
const fsPromises = require('node:fs/promises');

// 回調方式
fs.readdir('myDir', (err, files) => {
  if (err) throw err;
  console.log('目錄內容:', files);
});

// Promise 方式
fsPromises.readdir('myDir')
  .then(files => console.log('目錄內容:', files))
  .catch(err => console.error(err));

// 同步方式
const files = fs.readdirSync('myDir');
console.log('目錄內容:', files);

刪除目錄

const fs = require('node:fs');
const fsPromises = require('node:fs/promises');

fs.rm('dirWithContent', { recursive: true, force: true }, (err) => {
  if (err) throw err;
  console.log('目錄及其內容已刪除');
});

// Promise 方式
fsPromises.rm('dirWithContent', { recursive: true, force: true })
  .then(() => console.log('目錄及其內容已刪除'))
  .catch(err => console.error(err));

// 同步方式
fs.rmSync('dirWithContent', { recursive: true, force: true });

// 舊方法 (僅用於刪除空目錄)
fs.rmdir('emptyDir', (err) => {
  if (err) throw err;
  console.log('空目錄已刪除');
});

檔案資訊

const fs = require('node:fs');
const fsPromises = require('node:fs/promises');

// 獲取檔案詳細資訊
fs.stat('example.txt', (err, stats) => {
  if (err) throw err;
  console.log('檔案大小:', stats.size, '位元組');
  console.log('是否為檔案:', stats.isFile());
  console.log('是否為目錄:', stats.isDirectory());
  console.log('最後修改時間:', stats.mtime);
  console.log('創建時間:', stats.birthtime);
});

// Promise 方式
fsPromises.stat('example.txt')
  .then(stats => {
    console.log('檔案大小:', stats.size, '位元組');
    console.log('是否為檔案:', stats.isFile());
    console.log('是否為符號連結:', stats.isSymbolicLink());
  })
  .catch(err => console.error(err));

// 同步方式
try {
  const stats = fs.statSync('example.txt');
  console.log('檔案資訊:', stats);
} catch (err) {
  console.error('獲取檔案資訊時發生錯誤:', err);
}

檔案串流 (Streams)

適用於處理大檔案時避免記憶體溢出:

const fs = require('node:fs');
const { pipeline } = require('node:stream/promises');

// 創建讀取串流
const readStream = fs.createReadStream('largeFile.txt', 'utf8');

// 創建寫入串流
const writeStream = fs.createWriteStream('output.txt');

// 串流事件處理
readStream.on('data', (chunk) => {
  console.log(`接收到 ${chunk.length} 位元組的數據`);
});

readStream.on('end', () => {
  console.log('已完成讀取');
});

readStream.on('error', (err) => {
  console.error('讀取錯誤:', err);
});

// 使用 pipeline 進行串流導向
async function copyFile() {
  try {
    await pipeline(readStream, writeStream);
    console.log('檔案複製完成');
  } catch (err) {
    console.error('管道錯誤:', err);
  }
}

copyFile();

// 使用 pipe
readStream.pipe(writeStream);

其他還有很多,有興趣可以再去官網尋找

參考資料

node.js 官網


上一篇
Day4 - Node.js 建立專案
下一篇
Day6 - Node.js HTTP 模組
系列文
欸欸!! 這是我的學習筆記6
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言