node文件系统基础

最近在出差期间,很少有整块去学习,但还是尽量会抽时间来看点东西,特别是关于node一些相关知识。

关于node确实很难有实践的机会,以后会尽量将学习到关于node的一些知识逐个记录下来,也是为了自己以后能用到的时候能够快速复习。

今天会通过自己写的一个demo来记录一些关于node文件系统的一些知识点,相关api从文档中都可以查阅到,我一开始看的是一些教程及官方的英文文档,以下是中文文档链接,这个问题不大。
http://nodejs.cn/api/fs.html#fs_class_fs_writestream

Talk is cheap,show me the code.

var fs = require('fs')
var path = require('path')

function file (obj) {
  this.src = obj.src;
  this.targetDir = obj.targetDir;
  let fileType = this.fileType(this.src);
  this.filename = obj.filename + '.' + fileType;
  this.dst = path.join(this.targetDir, this.filename);
  this.copyStart();
}

file.prototype = {
  // 返回读取文件的文件格式
  fileType: function (pathName) {
    let reg = /[.]/g;
    let index = 0;
    while (reg.exec(pathName) !== null) {
      index = reg.lastIndex;
    }
    let fileType = pathName.slice(index);
    return fileType;
  },
  // 查看复制的新文件命名在对应文件路径内是否存在
  fileExists: function () {
    return fs.existsSync(this.dst)
  },
  // 开始复制
  copyStart: function () {
    if (this.fileExists()) {
      console.error(this.filename + '在' + this.targetDir + '目录下已经存在');
    } else {
      this.copy();
    }
  },
  // 复制过程
  copy: function () {
    let rs = fs.createReadStream(this.src);
    let ws = fs.createWriteStream(this.dst);
    rs.on('data', function (chunk) {
      if (ws.write(chunk) === false) {
        rs.pause();
      }
    });

    rs.on('end', function () {
      ws.end();
      console.log('读写结束')
    });

    ws.on('drain', function () {
      console.log('缓存写完一次,开始下一次')
      rs.resume()
    })
  }
}

  // 文件复制
{

  let newFileName = 'fuckText'; // 新文件命名
  let src = './static/Delacey - Dream It Possible.mp3';
  let dir = './dst';
  // console.log(path.extname(src))
  new file({
    src: src,
    targetDir: dir,
    filename: newFileName
  });
}

通过上述demo,直接将file构造函数实例化即可完成复制文件,其中已经包括文件是否重复的校验及后缀自动添加。

一、先介绍一点概念和api

  1. node是没有文件拷贝这种高级功能的,所以我们拿文件拷贝来作为demo学习,首先关于文件系统的操作,我们一定要引入node自身提供用于处理文件系统的包fs,由于涉及一些路径上字符串的合并我们还引入了第二个自带包path用于处理一些路径的合并。

  2. 小文件直接使用api fs.writeFileSync(dst, fs.readFileSync(src))即可实现,一般比较小的纯文本都不会造成缓存写入时的阻塞,所以不需要用流的形式,但我们此处为了通用性,demo采用数据流的形式来实现文件拷贝,值得一提的是 fs.readFileSync(src)这个api是同步读取制定文件,注意是只读,读出来的数据以数据块(buffer)的形式存在内存中,buffer主要实现了对二进制数据的操作,你可以轻松的将buffer的实例化对象通过toString('utf-8')从其他格式转换为utf-8的格式,我现在的个人理解就是node是通过buffer储存与转换数据的,并且以二进制的形式储存在内存中,简单来说就是数据块。

  3. 什么情况下使用数据流呢,很简单,当拷贝或传输文件过大时。在大文件的传输过程中,很容造成内存的爆仓,如果仍然使用一次性传输,我们很容易出现读写速度不一致的现象,简单来说就是读取的数据buffer块暂时会放不下,但此时又因为没读取完当然不能进行写的操作,那么此时就可以说爆仓了,这个时候就出现了数据流的概念,就是一边写一边读,先读一部分,内存满了,开始写,写完了就继续读,直至循环读写完毕,其中是利用pipe管道流的概念完成,就像这张图一样这里写图片描述

二、解释一下整个函数的思路

  1. 直接实例化对象file传入 文件来源路径src, 目标目录targetDir, 新文件名 filename便可实现整个复制操作,demo写的比较粗糙,用的就是原型模式,构造函数内用于记录数据,函数的操作都写在了原型中,在构造函数file中,我们首先进行的是必要数据的赋值及初始化处理,利用fileType函数对src进行分析,剖解出来源文件的文件后缀类型,完成文件名filename的赋值,随后通过path.join将目标目录与目标文件名合并为最后的目标文件路径dst,最终执行copyStart函数;

  2. 接下来解读原型中函数的作用

    • fileType函数利用正则匹配出需要赋值文件的文件格式并返回;
    • fileExists函数利用fs.existsSync(path)这个api同步确定新文件是否在对应路径内已存在,存在返回true,否则为false;
    • copyStart函数,开始执行复制行为,将复制过程与复制发起者分离;
    • copy函数,整个复制demo的关键,利用数据流的形式边读边写;
  3. 详细解释copy函数的过程

    • 创建只读流rs与只写流ws,通过对只读流的data事件监听文件的读取过程,end事件监听读取的结束;
    • 刚才我们说过一次性读写会出现爆仓或文件复制不完整的情况,所以我们要适当对只写流进行监听,在只写流还没写完当前内存中的数据块时,我们要将只读流暂时暂停,这时我们就需要在rsdata事件中利用ws.write(chunk)判断当前内存的数据块是写入目标了,还是还存在于内存内,这里的内存可以解释为缓存,若没写完,则我们先暂停只读流的读取rs.pause();
    • 当只写流将当前内存内的数据块写完后我们需要重启只读流开始下一次数据块的读取,所以我们需要在外部利用wsdrain事件进行监听,每当将内存中的数据块写完一次,即流每次完成时会触发,在内部我们需要再次恢复只读流的读取rs.resume();
    • 最后当来源文件被整体读取完毕并缓存中没有数据块时就会触发只读流的end事件了,这时我们利用ws.end()来终结最后的写入,完成复制;

相关事件的详细描述请参见文档,还有很多api我并没有在此介绍,在需要时翻文档就好,对了,其实path.extname(src)这个api可以直接检测文件的格式= =。。。根本不需要我上面的fileType函数,一开始我没发现这个函数才写的,各位莫怪;

由于我也在学习阶段,如果文中有什么错误,欢迎指出纠正,最后献上一个不错的初步node教程。
http://nqdeng.github.io/7-days-nodejs/#3.2.2

猜你喜欢

转载自blog.csdn.net/yolo0927/article/details/70524155