nodeJS文件流和事件


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);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM