Node.js 檔案函式庫
Node.js 平台所提供的函式庫,除了和網站設計有關的部分之外,還有一些是和輸出入有關的功能,這類輸出入功能是不能在瀏覽器內使用的,因此也沒有被包含到 JavaScript 的標準函式庫中,但在本書中我們會常常使用到,您可以從 node.js 官網當中閱讀有關這些函數的 API (Application Interface)
- node.js 的官方系統 API 位於
建議讀者可以花些時間看看上述這些比較常用且重要的函式庫,先瀏覽之後建立一點概念就行了,這樣等到你需要用到時,就會知道去哪裡找出你要的函數了。
除此之外,開放原始碼社群,像是 node.js 官方的 npm 套件管理器當中,還包含了數十萬個套件供您使用,因此我們可以使用的資源是非常多的。
當然、我們不可能把這些套件通通都看過,甚至連列出來都有困難,因此我們必須學習如何尋找出需要的套件,這樣的能力在這個開放原始碼主導的程式世界裏,變得愈來愈重要了!
檔案與輸出入
由於 JavaScript 語言一開始是內嵌在《網頁》裡面使用的,所以《預設函式庫》裏沒有檔案輸出入的功能,但是 node.js 系統裏提供了很多檔案相關的功能可以使用。
接下來這段解釋我們用了很多專有名詞,如果看不懂也沒關係,看不懂就把它當成《克林貢外星語》直接跳過好了!
Node.js 的檔案輸出入設計非常的特別,這是因為 node.js 一開始就希望能夠作為有效率的 server 程式框架,因此檔案輸出入通常都採用《非同步》(non synchronize) 的模式,這種方式通常採用《非阻斷》(non blocking) 的執行法,然後大量利用《回呼函數》(callback function) 的方式呼叫,這種方法可以不需要採用《線程》(執行緒, thread) 或《多行程》(multiprocess) 就可以非常有效的扮演好 server 的角色,但是卻付出了對應的代價,那就是程式碼會變得很難看。
還好很多技術可以讓你稍微緩解《這種由回乎所造成的程式碼難看問題》,像是 async、promise、yield/generator 等等
如果你真的不想用回呼版本的呼叫, node.js 裏也通常提供了《同步》(synchronous) 版本,只是該版本的函數名稱會比較長,而且會因為《阻斷性呼叫》(blocking) 而造成效能上的問題而已,這對初學者而言,有時候是可以接受的。
講了這麼多《克林貢外星語》,還是讓我們真正來看一些程式好了,等看完程式之後,你再回過頭來看這段描述,可能就比較能理解了!
檔案讀取
檔案:readfile.js
var fs = require('fs'); // 引用檔案物件
var data = fs.readFileSync(process.argv[2], "utf8"); // 讀取檔案
console.log(data); // 顯示在螢幕上
檔案讀取 (非阻斷回呼型)
檔案: readfileCallback.js
var fs = require('fs'); // 引用檔案物件
fs.readFile(process.argv[2], "utf8", function(err, data) {
console.log("data="+data);
});
console.log("----readFile End-----"); // 顯示在螢幕上
檔案寫入
檔案:copyfile.js
var fs = require('fs');
var data = fs.readFileSync(process.argv[2]);
console.log(data);
fs.writeFileSync(process.argv[3], data);
檔案寫入 (非阻斷回呼型)
檔案:copyfileCallback.js
var fs = require('fs');
fs.readFile("copyfileCallback.js", "utf8", function(err, data) {
console.log('讀取完成!');
fs.writeFile("copyfileCallback.js2", data, function(err) {
console.log('寫入完成!');
});
});
多層回呼
檔案:copyfile2.js
var fs = require('fs');
var data = fs.readFileSync('copyfile2.js');
console.log("=======copyfile.js========");
console.log(data.toString());
fs.writeFileSync("copyfile2.js2", data);
var data2 = fs.readFileSync("copyfile2.js2");
console.log("=======copyfile2.js========");
console.log(data.toString());
fs.writeFileSync("copyfile2.js3", data);
檔案:copyfileCallback2.js
var fs = require('fs');
fs.readFile("copyfileCallback2.js", "utf8", function(err, data) {
console.log('讀取完成!');
fs.writeFile("copyfileCallback2.js2", data, function(err) {
console.log('寫入完成!');
fs.readFile("copyfileCallback2.js2", "utf8", function(err, data) {
console.log('又讀取完成 !');
fs.writeFile("copyfileCallback2.js3", data, function(err) {
console.log('又寫入完成!');
});
});
});
});
執行結果
D:\Dropbox\cccwd2\file\jsh\code\file>node copyfileCallback2.js
讀取完成!
寫入完成!
又讀取完成 !
又寫入完成!
使用 yield 避免多層式回呼
檔案: copyfileYield2.js
var fs = require('mz/fs');
var co = require('co');
co(function*() {
var data1=yield fs.readFile("copyfileYield2.js", "utf8");
console.log('讀取完成!');
yield fs.writeFile("copyfileYield2.js2", data1);
console.log('寫入完成!');
var data2 = yield fs.readFile("copyfileYield2.js2", "utf8");
console.log('又讀取完成 !');
yield fs.writeFile("copyfileYield2.js3", data2);
console.log('又寫入完成!');
});
執行本範例前必須先安裝 co 與 mz 套件
執行結果
D:\Dropbox\cccwd2\file\jsh\code\file>npm install co
D:\Dropbox\cccwd2\file\jsh\code\file>npm install mz
D:\Dropbox\cccwd2\file\jsh\code\file>node copyfileYield2
讀取完成!
寫入完成!
又讀取完成 !
又寫入完成!
使用 Async 避免多層回呼
檔案: copyFileAsync2.js
var fs = require('fs');
var async = require('async');
var text = null;
async.series([
function(callback) {
fs.readFile("copyfileAsync2.js", "utf8", function(err, data) {
console.log('讀取完成!!');
text = data;
callback();
})
},
function(callback) {
fs.writeFile("copyfileAsync2.js2", text, function(err) {
console.log('寫入完成!');
callback();
})
},
function(callback) {
fs.readFile("copyfileAsync2.js2", "utf8", function(err, data) {
console.log('又讀取完成 !');
text = data;
callback();
})
},
function(callback) {
fs.writeFile("copyfileAsync2.js3", text, function(err) {
console.log('又寫入完成!');
callback();
})
}
], function done() {
console.log('全部完成!');
});
執行本範例前必須先安裝 async 套件!
執行結果
D:\Dropbox\cccwd2\file\jsh\code\file>npm install async
D:\Dropbox\cccwd2\file\jsh\code\file>node copyfileAsync2.js
讀取完成!!
寫入完成!
又讀取完成 !
又寫入完成!
全部完成!
從鍵盤讀取輸入
參考 – http://nodejs.org/api/readline.html
檔案:readline.js (讀取一行)
var readline = require('readline');
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.question("What do you think of node.js? ", function(answer) {
// TODO: Log the answer in a database
console.log("Thank you for your valuable feedback:", answer);
rl.close();
});
檔案:readloop.js (讀取很多行)
var readline = require('readline');
var rl = readline.createInterface(process.stdin, process.stdout);
rl.setPrompt('OHAI> ');
rl.prompt();
rl.on('line', function(line) {
switch(line.trim()) {
case 'hello':
console.log('world!');
break;
default:
console.log('Say what? I might have heard `' + line.trim() + '`');
break;
}
rl.prompt();
}).on('close', function() {
console.log('Have a great day!');
process.exit(0);
});
習題
- 請設計一個程式,可以印出某資料夾中的所有檔案名稱。例如:
- $ node listdir.js somedir
- 這樣就會印出 somedir 中的所有檔案。
- 請設計一個程式,可以把指定資料夾複製一份,到目標資料夾當中。例如:
- $ node copydir.js fromdir todir
- 這樣就會把 fromdir 內的所有內容,複製到 todir 當中
- 請設計一個像 node shell 的交談環境程式
解答:
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
prompt: '> '
});
rl.prompt();
rl.on('line', (line) => {
try {
console.log(eval(line));
} catch (e) {
console.error(e.stack);
}
rl.prompt();
}).on('close', () => {
process.exit(0);
});