• 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);
  • 相关阅读:
    2017.08.07 python爬虫实战之使用代理爬取糗事百科
    2017.08.05 Python网络爬虫实战之获取代理
    2017.08.04 Python网络爬虫之Scrapy爬虫实战二 天气预报的数据存储问题
    2017.08.04 Python网络爬虫之Scrapy爬虫实战二 天气预报
    2017.07.28 Python网络爬虫之爬虫实战 重新梳理------今日影视爬虫(使用Scrapy爬取ajax动态页面)
    2017.07.28 Python网络爬虫之爬虫实战 今日影视2 获取JS加载的数据
    RabbitMQ之工作队列
    pt-online-schema-change和默认值关系
    flex词法解析
    Makefile持续学习二
  • 原文地址:https://www.cnblogs.com/lyraLee/p/12178437.html
Copyright © 2020-2023  润新知