美文网首页nodejs
03、NodeJS-文件操作

03、NodeJS-文件操作

作者: EndEvent | 来源:发表于2017-07-06 08:09 被阅读327次

    一、核心模块和对象

    • 核心模块的意义
      - 如果只是在服务器运行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-文件操作

        本文链接:https://www.haomeiwen.com/subject/hrnpfxtx.html