03、NodeJS-文件操作

一、核心模块和对象

  • 核心模块的意义
  - 如果只是在服务器运行JavaScript代码,意义并不大,因为无法实现任何功能(读写文件,访问网络)。
  - Node 的用处在于它本身还提供的一系列功能模块,用于与操作系统互动。
  - 这些核心的功能模块在 Node 中内置。
  • 常用内置模块
    • path:处理文件路径
    • fs:操作文件系统
    • child_process:新建子进程
    • util:提供一系列实用小工具
    • http:提供HTTP服务器功能
    • url:用于解析URL
    • querystring:解析URL中的查询字符串
    • crypto:提供加密和解密功能
    • 其他

二、文件系统操作

  • 相关模块的使用
  - fs: 基础的文件操作 API
  - path: 提供和路径相关的操作 API
  - readline: 用于读取大文本文件,一行一行读
  - fs-extra(第三方文件操作模块): https://www.npmjs.com/package/fs-extra 
  • path模块的使用
    在文件操作的过程中,都必须使用物理路径(绝对路径),path模块提供了一系列与路径相关的 API;
  // path.basename(path[, ext])
  // 获取文件名(包含后缀)
  console.log(path.basename(temp));
  // 获取文件名(不包含后缀) 第二个参数即要删除的后缀
  console.log(path.basename(temp, '.lrc'));
  
  // path.delimiter
  // 获取不同操作系统中默认的路径分隔符
  // windows是分号,Linux是冒号
  console.log(path.delimiter);
  // 获取环境变量
  console.log(process.env.PATH); 
  // 将对应环境变量分割
  console.log(process.env.PATH.split(path.delimiter));

  // path.dirname(path)
  // 获取目录名称
  console.log(path.dirname(temp));

  // path.extname(path)
  // 获取后缀名,包含点(在path.basename中就有ext)
  console.log(path.extname(temp));

  // path.parse(path)
  // 将路径字符串转为对象(文件根目录、文件路径、文件全名(带后缀)、后缀名、文件名(不带后缀))
  console.log(path.parse(temp));

  // path.format(pathObject)
  // 路径对象转字符串
  console.log(path.format( path.parse(temp) ));

  // path.isAbsolute(path)
  // 判断是否为绝对路径
  console.log(path.isAbsolute(temp));

  // path.join([...paths])
  // 路径拼接
  const temp = path.join(__dirname, './lyrics/相依为命.lrc');

  // path.normalize(path)
  // 常规化一个路径(将多余的斜杠处理,)
  var urlStr = 'C:/System32\\a//test///haha.html';
  console.log(path.normalize(urlStr));

  // path.relative(from, to)
  // 获取to相对于from的相对路径
  console.log(path.relative(__dirname, '/Users/test/Desktop/03-文件系统操作/haha.html'));

  // path.resolve([...paths])
  // 类似于join的字符串拼接
  console.log(path.resolve(__dirname, '../', '../', 'haah.html'));
  // 与join不一样的(不是简单的字符串拼接)
  console.log(path.resolve(__dirname, '/User/test', 'hehe', 'haha.html'));  // 输出结果是/User/test/hehe/haha.html
  • 同步或异步调用
  - fs模块对文件的几乎所有操作都有同步和异步两种形式
  - 例如:readFile() 和 readFileSync()
  - 同步调用会阻塞代码的执行,异步则不会
  - 异步调用会将读取任务下达到任务队列,直到任务执行完成才会回调
  - 异常处理方面,同步必须使用 try catch 方式,异步可以通过回调函数的第一个参数
  • 文件读取
    • 异步文件读取
      fs.readFile(file[, options], callback(error, data))
// 读取时有设置编码utf8
fs.readFile(path.join(__dirname, './test.txt'), 'utf8', (error, data)=>{
      if(error) throw error;
      console.log(`异步: ${data}`);
});
  • 同步文件读取
    fs.readFileSync(file[, options])
// 同步操作的写法,错误是通过抛出异常
try {
      var data = fs.readFileSync(path.join(__dirname, './test.txt'), 'utf8');
      console.log(`同步: ${data}`);
} catch (error) {
      throw  error;
}

node默认编码类型是utf8;
由于Windows平台下默认文件编码是GBK,在node中是不支持的gbk类型编码的!!!!
对于各种操作系统中编码问题,可以通过iconv-lite解决

  • 文件流的方式读取
    fs.createReadStream(path[, options])
const stream = fs.createReadStream('1.txt');
let data = ' '
stream.on('data', (trunk) => {
    data += trunk;
});
stream.on('end', () => {
    console.log(data);
});

流的方式Stream模块。pipe管道,可以将读取到流,导入到另外一个目标中,还可以设置流的类型;
fs.createReadStream(filename)假如该文件流数据类型是utf8 通过pipe,将流的类型改变gbk fs.createReadStream(filename).pipe(iconv.decodeStream('gbk'))

  • 模块逐行读取文本
    readline
    const readline = require('readline');
    const fs = require('fs');
      
    const rl = readline.createInterface({input: fs.createReadStream('sample.txt') });
    
    rl.on('line', (line) => {
        console.log('Line from file:', line);
  });

正则表达式: objReg.exec(string) 该函数通过对指定你的字符串进行一次匹配检测,获取字符串中的第一个与正则表达式的内容,并且将匹配的内容和子匹配的结果存放在返回数组中
var rel = /^\[\d{2}\:\d{2}\.\d{2,4}\].+$/;
var rel = /^\[(\d{2})\:(\d{2})\.(\d{2,4})\](.+)$/; // 有括号的即为子分组
rel.exec(line)

> 案例: 歌词滚动

案例: 根据歌曲时间显示对应歌词

  • 文件写入
    • 异步文件写入
      fs.writeFile(file,data[,option],callback(err))
  fs.writeFile(path.join(__dirname, './temp.txt'), JSON.stringify({id: 10}), (err) => {
    if(err){
        // 一般写文件,如果出现错误,都是因为权限问题(权限不够不能创建文件)
        // 文件夹如果不存在,不会创建文件夹,也会出错
        console.log('err:' + err);
    } else {
        console.log('文件写入成功');
    }
});
  • 同步文件写入
    fs.writeFileSync(file,data,[,option])
  fs.writeFileSync(path.join(__dirname, 'temp.txt'), '你好?');
  • 流式文件写
    fs.createWriteStream(path[,option])
fs.createWriteStream();
var streamWriter = fs.createWriteStream(path.join(__dirname, 'temp.txt'));
  // 是属于非阻塞的streamWriter.write,返回true/false
 console.log( streamWriter.write('哈哈', ()=>{
      console.log('+1');
}) );

默认写入操作是覆盖源文件

  • pipe管道方式(文件读写操作)
var readstream = fs.createReadStream('01-path.js');
var writestream = fs.createWriteStream('test4.txt');
// 直接将读文件流 导入到 写文件流中
readstream.pipe(writestream);
  • 异步追加
    fs.appendFile(file,data[,options],callback(err))
fs.appendFile(path.join(__dirname, 'temp.txt'), ' 测试', (err)=>{
    if(err){
        console.log('error: ' + err);
    } else {
        console.log('追加成功');
    }
});
  • 同步追加
    fs.appendFileSync(file,data[,options])
fs.appendFileSync(path.join(__dirname, 'temp.txt'), ' 追加成功了吗?');
  • 文件其他操作
    • 获取文件信息
      fs.stat(path,callback(err,stats))
      fs.statSync(path) // => 返回一个fs.Stats实例
fs.stat('temp.txt', (err, stats)=>{
    if(err){
        console.log('err:' + err);
    } else {
        console.log(stats.isFile());
    }
});

读取到文件信息后,可以判断文件的类型
stats.isFile()
stats.isDirectory()

stats.isBlockDevice()
stats.isCharacterDevice()
stats.isSymbolicLink()
stats.isSocket()

  • 移动文件、重命名文件或目录
    fs.rename(oldPath,newPath,callback)
    fs.renameSync(oldPath,newPath)
  fs.rename('temp.txt', 'test.txt', (err)=>{
    if(err){
        console.log('err:' + err);
    } else {
        console.log('修改文件名成功');
    }
});
  • 删除文件
    fs.unlink(path,callback(err))
    fs.unlinkSync(path)
fs.unlink('test.txt', (err)=>{
    if(err){
        console.log('err:' + err);
    } else {
        console.log('删除文件名成功');
    }
});
  • 目录操作
    • 创建一个目录
      fs.mkdir(path[,model],callback)
      fs.mkdirSync(path[,model])
fs.mkdir(path.join(__dirname, 'test.txt'), (err)=>{
    if(err){
        console.log(err);
    } else {
        console.log('创建文件成功');
    }
});
  • 删除一个空目录
    fs.rmdir(path,callback)
    fs.rmdirSync(path)
fs.rmdir(path.join(__dirname, 'test.txt'), (err)=>{
         if(err){
            console.log('err:' + err);
         }else{
             console.log('删除成功');
         }
 });
  • 读取一个目录
    fs.readdir(path,callback(err,files))
    fs.readdirSync(path) // => 返回files
fs.readdir(__dirname, (err, files)=>{
           if(err){
            console.log('err:' + err);
           }else{
             console.log(files);
           }
});

三、缓冲区

  • 什么是缓冲区
  - 缓冲区就是内存中操作数据的容器  
  - 只是数据容器而已
  - 通过缓冲区可以很方便的操作二进制数据
  - 而且在大文件操作时必须有缓冲区
  • 为什么要有缓冲区
    JavaScript是比较擅长处理字符串,但是早期的应用场景主要用于处理HTML文档,不会有太大篇幅的数据处理,也不会接触到二进制的数据。而在Node中操作数据、网络通信是没办法完全以字符串的方式操作的,所以在Node中引入了一个二进制的缓冲区的概念:Buffer。

  • 缓冲区简单操作

  // 创建长度为4个字节的缓冲区
  var buffer = new Buffer(4);

  // 通过指定编码的方式创建
  var buffer = new Buffer('hello', 'utf8');

  // 往缓冲区中写数据(假如缓冲区只有4个字节大小)
  buffer.write('123456');
  console.log(buffer.toString('utf8'));  // 输出是1234

node中默认的编码类型都是utf-8;

  • 读图片操作
  var fs = require('fs');
  var path = require('path');

  fs.readFile(path.join(__dirname, './test.png'), (error, data)=>{
      if(error) console.error(error);
      // 将buffer内容读取出来(图片是base64)
      console.log(data.toString('base64'));
  });

还原生成Base64编码为图片: http://www.atool.org/img2base64.php;
注意: 要在前面添加上data:image/png;base64,表示图片类型;

  • node支持的编码
    Buffers 和 JavaScript 字符串对象之间转换时需要一个明确的编码方法。
  - 'ascii' - 7位的 ASCII 数据。这种编码方式非常快,它会移除最高位内容
  - 'utf8' - 多字节编码 Unicode 字符。大部分网页和文档使用这类编码方式
  - 'utf16le' - 2个或4个字节, Little Endian (LE) 编码 Unicode 字符。编码范围 (U+10000 到 U+10FFFF) 
  - 'ucs2' - 'utf16le'的子集
  - 'base64' - Base64 字符编码
  - 'binary' - 仅使用每个字符的头8位将原始的二进制信息进行编码
(在需使用 Buffer 的情况下,应该尽量避免使用这个已经过时的编码方式,这个编码方式将会在未来某个版本中弃用)。
  - 'hex' - 每个字节都采用 2 进制编码。

在node中是不支持的gbk类型编码的!!!!
对于各种操作系统中编码问题,可以使用第三方模块iconv-lite

你可能感兴趣的:(03、NodeJS-文件操作)