1. 普通文件拷貝
文件拷貝的原理是通過fs.readFile從一個文件讀取內容,然后通過fs.writeFile將其寫入另一個文件。
readFile會默認將文件內容全部讀取到內存中,然后再寫入另一個文件。
let fs = require('fs'); //fs即file system let path = require('path'); /* 1. 讀取文件使用絕對路徑; 2. 讀取的內容全部讀取到內存中; */ // 異步讀取文件 fs.readFile(path.resolve(__dirname, './1.txt'), (err,data) => { // 寫入文件;如果對應路徑上文件不存在,會自動創建一個文件 fs.writeFile(path.resolve(__dirname, './2.txt'), data, (err) => { console.log("寫入成功"); }); })
但是這種拷貝文件的方式,適用與文件較小時(小於64k)。當大於64k時,會出現性能問題。通常會希望文件邊讀邊寫。
這就需要文件流。
2. 事件模塊events
文件流基於事件。
// 手動實現一個events模塊 模擬let EventEmitter = require('events'); class EventEmitter{ constructor() { // {eventName: [callback1, callback2],....} this._events = {}; } // 訂閱 on(eventName, callback) { if(this._events[eventName]) { this._events[eventName].push(callback); } else { this._events[eventName] = [callback]; } } // 發布 emit(eventName) { this._events[eventName].forEach(fn => { fn(); }); } // removeListener off(eventName, callback) { this._events[eventName] = this._events[eventName].filter(fn => fn !== callback) } } // 應用 const e = new EventEmitter(); let eatFood = () => { console.log('eat food'); } let eatFruit = () => { console.log('eat fruit'); } e.on('eat', eatFood); e.on('eat', eatFruit); e.emit('eat'); /* eat food eat fruit */ e.off('eat', eatFood); e.emit('eat'); // eat fruit
3. 文件流
fs模塊提供了流操作的API。
流分為四類:可讀流、可寫流、雙工流(可讀可寫)、轉換流(壓縮文件)
1. 可讀流
可讀流的作用:
1. 可以分段讀取文件
2. 可以控制讀取的速率和范圍
可讀流主要依賴於fs.createReadStream()方法。 實例訂閱on('data'), on('end')事件。涉及Buffer.concat()方法。
const fs = require('fs'); const path = require('path'); // 相當於創建可讀流實例:new ReadStream const rs = fs.createReadStream(path.resolve(__dirname,'./1.txt'), { flags: 'r', highWaterMark: 2, //每次最多讀取的字節數;默認64k start: 0, end: 10, //[start, end]設置讀取范圍 autoClose: true, //讀取完成后關閉文件 encoding: true }); // 內部監聽data訂閱,如果監聽到,內部觸發rs.emit('data');然后on的回調函數才執行。是異步操作。 let arr = [];// 存儲二進制代碼段 rs.on('data', function(chunk) { console.log(chunk) arr.push(chunk); rs.pause(); // 暫停讀取 }); // 每2秒讀取一次 let timer = setInterval(() => { rs.resume(); },2000) // 監聽完成事件 rs.on('end', function() { timer = null; clearInterval(timer); console.log(Buffer.concat(arr).toString()); })
2. 可寫流
可寫流可以控制每次寫入的大小。主要有write(),end()方法。
const fs = require('fs'); const path = require('path'); let ws = fs.createWriteStream(path.resolve(__dirname, './2.txt'), { flags: 'w', // 如果文件不存在,則創建;如果已經有內容,則先清空。 encoding: 'utf8', highWaterMark: 3, // 預計每次寫入的字節數;默認16k start: 0, // 起始寫入的位置 autoClose: true // 寫完后關閉文件 }) // write只能寫入字符串或者buffer let flag = ws.write('abcdef', function(err) { console.log('寫入成功'); }); console.log(flag); //寫入的長度大於highWaterMark ws.end('結束'); // end方法之后不能再調用ewrite方法
4 . 文件拷貝 = 可讀流+可寫流
通過流實現文件拷貝。主要pipe方法。避免全部讀取到內存后再寫入的情況。
const fs = require('fs'); const path = require('path'); let rs = fs.createReadStream(path.resolve(__dirname, './1.txt'), { highWaterMark: 3 }); let ws = fs.createWriteStream(path.resolve(__dirname, './2.txt'), { highWaterMark: 2 }); rs.pipe(ws); // 模擬實現pipe方法 function pipe(r,w) { rs.on('data', function(chunk) { let flag = ws.write(chunk); if (!flag) rs.pause(); }); // 單次寫入完成 ws.on('drain', function(){ rs.resume(); console.log("抽空"); }) rs.on('end', function(err) { console.log('文件讀取完畢'); ws.end(); }) } // pipe(rs,ws);