初学Nodejs(3):文件系统fs模块+path路径模块

初学Nodejs

文件系统

1、概述

在node中,与文件系统的交互是非常重要的,服务器的本质就是将本地的文件发送给远程的客户端。

node通过fs模块来和文件系统进行交互,该模块提供了一些标准文件访问API来打开、读取、写入文件,以及与其交互。要使用这个模块就需要对齐进行导入,const fs = require('fs');

fs模块是核心模块,直接引入,无需下载。
官网fs模块地址

2、文件系统的使用

2.2 文件写入

2.2.1 同步文件的写入

从上往下依次执行,并且只有当文件写入完成时才会继续执行下面的代码

  • 打开文件
    • fs.openSync(path,flags,[,mode]) 方法
      • path:要打开文件的路径
      • flags:打开文件要做的操作的类型(r:只读;w:可写)
      • mode:设置文件的操作权限,一般不传
      • 返回值:返回一个文件的描述符作为结果,可以通过该描述符来对文件进行各种操作。

如果要打开的文件不存在,那么会先创建文件

  • 向文件中写入内容

    • fs.writeSync(fd,string[,position[,ecoding]]) 方法
      • fd:文件的描述符,需要传递要写入的文件的描述符
      • string:要写入的内容
  • 保存并关闭文件

    • fs.closeSync(fd)
      • fd:文件描述符
// 引入fs模块
var fs = require('fs');
// 打开指定文件,指定权限
var fd = fs.openSync('hello.txt','w');
// 向文件中写入内容
fs.writeSync(fd,'你好呀');
// 保存并关闭文件
fs.closeSync(fd);
2.2.2 异步文件的写入
  • 打开文件
    • fs.open(path,flags[,mode],callback)
      • path:文件路径
      • flags:打开文件要做的操作的类型(r:只读;w:可写)
      • mode:设置文件的操作权限,一般不传
      • 返回值:异步文件都是通过回调函数的参数返回的
        – 回调函数的两个参数:
        (1)err,错误对象,如果没有错误则为null
        (2)fd,文件描述符

{flag:‘xxx’},xxx的可选择的值如下:

模式说明
r读取文件,文件不存在则出现异常
r+读写文件,文件不存在则出现异常
rs在同步模式下打开文件用于读取
rs+在同步模式下打开文件用于读写
w打开文件用于写操作,如果不存在则创建,如果存在则替换原内容
wx打开文件用于写操作,如果存在则打开失败
w+打开文件用于读写,如果不存在则创建,如果存在则截断
wx+打开文件用于读写,如果存在则打开失败
a打开文件用于追加,如果存在则打开失败
ax打开文件用于追加,如果路径存在则失败
a+打开摁键进行读取和追加,如果不存在则创建该文件
ax+打开文件进行读取和追加,如果路径存在则失败
  • 写入文件

    • fs.write(fd,string[,postion[,encoding]],callback)
      • 用来异步写入一个文件
  • 关闭文件

    • fs.close(fd,callback)
var fs = require('fs');

// 打开文件
/**
fs.open('world.txt','w',function(){
	console.log(arguments); // [Arguments] { '0': null, '1': 3 }
})
*/

fs.open('world.txt', 'w', function (err, fd) {
	// 判断是否出错,没出错就进行写入操作
    if (!err) {
        fs.write(fd, '这是异步写入的内容', function (err) {
            if (!err) {
                console.log('写入成功!');
            }

            // 关闭文件
            fs.close(fd, function (err) {
                if (!err) {
                    console.log('文件已关闭');
                }
            })
        })
    } else {
        console.log(err)
    }
})

异步不会阻塞程序的向下执行

2.2.3 简单文件写入

其实就是对上面两个写法进行了依次封装,将open和write封装到了一个函数里面。

API:

同步:fs.writeFile(file,data[,options],callback)
异步:fs.writeFileSync(file,data[,options])
options参数表示以什么格式写入文件内容,默认是utf8

// 导入fs模块
var fs = require('fs');

// 同步写入
// fs.writeFileSync('java.txt','这是通过writeFileSync')

// 异步写入
fs.writeFile('javascript.txt','这是通过writeFile写入的内容',{flag:'a'},function(err){
    if(!err){
        console.log('写入成功');
    }else{
        console.log(err);
    }
})

// {flag:'a'}表示写入文档的内容会自动追加到文档后面
2.2.4 流式文件写入

前面几个写入方式都是一次性将要写入的内容写入,都不适合大文件的写入,性能较差,容易导致内存溢出,而流式文件写入可以连续写入内容。

API

创建一个可写流
fs.creataWriteStream(path[,options])

  • 参数
    path:文件路径
    options:配置参数
var fs = require('fs')

// 创建一个可写流
var ws = fs.createWriteStream('what.txt')

// 通过监听流的open和close事件来监听流的打开和关闭
ws.once('open',function(){
	console.log('流打开了')
})

ws.once('close',function(){
	console.log('流打开了')
})

// 通过ws向文件中输出内容
ws.write('通过可写流写入文件的内容1')
ws.write('通过可写流写入文件的内容2')
ws.write('通过可写流写入文件的内容3')

// 关闭流
ws.end();

2.3 文件读取

2.3.1 同步/异步文件读取

API

同步:只有当文件读取完成时才会执行后面的代码
(1)fs.readSync(fd, buffer, offset, length[, position])
(2)fs.readSync(fd, buffer[, options])


异步:文件没有读取完时也可以执行后面的代码
(1)fs.read(fd, buffer, offset, length, position, callback)
(2)fs.read(fd[, options], callback)
(3)fs.read(fd, buffer[, options],callback)

  • 同步
const fs = require('fs')

var fd = fs.openSync('hello.txt','r')

// 创建一个buffer数组,存储读取到的内容
var buf = Buffer.alloc(10)
console.log(buf); // <Buffer 00 00 00 00 00 00 00 00 00 00>

// 将读取到的内容放到创建的buffer中,返回值为一个number数字,表示文档中的字节数
var readContent = fs.readSync(fd,buf)

console.log(buf); //  <Buffer e4 bd a0 e5 a5 bd e5 91 80 00>
console.log(buf.toString()); // 你好呀

console.log(readContent); // 9

fs.closeSync(fd)
2.3.2 简单文件读取

API

(1)同步:fs.readFileSync(path[,options])
(2)异步:fs.readFile(path[,options],callback)
options参数表示以什么格式写入文件内容,默认是utf8

以异步简单读取为例(常用):

const fs = require('fs')
fs.readFile('hello.txt',function(err,data){
  if(err){
      console.log(err);
  }else{
    //   console.log(data); // 十六进制结果
      console.log(data.toString()); // 你好呀
      // 把读取到的数据再写入到text.txt文件中
      fs.wirteFile('text.txt',data,function(err){
	      if(err){
	      	console.log(err);
	      }else{
	      	console.log('文件写入成功');
	      }
  })
})
2.3.3 流式文件读取

适合读取大文件,分多次读取大文件到内存中

const fs = require('fs')

// 创建流式文件读取对象
const rs = fs.createReadStream('hello.txt')
// 创建流式文件写入对象
const ws = fs.createWriteStream('hello2.txt')

// 监听流的开启和关闭
rs.once('open',function(){
    console.log('可读流打开了');
})
rs.once('close',function(){
    console.log('可读流关闭了');

    // 关闭可写流
    ws.end()
})

ws.once('open',function(){
    console.log('可写流打开了');
})
ws.once('close',function(){
    console.log('可写流关闭了');
})


// 如果要读取一个可读流中的数据,必须要为可读流绑定一个data事件,data事件绑定完毕,他会自动开始读取数据
rs.on('data',function(data){
    console.log(data);
    console.log(data.toString());
    // 写入数据
    ws.write(data)
})

结果:
可读流打开了
可写流打开了
<Buffer e4 bd a0 e5 a5 bd e5 91 80>
你好呀
可读流关闭了
可写流关闭了

适合读取大文件,每次分批读取文件内容(一次大概读取65536个字节)

简单写法:

const fs = require('fs')

// 创建流式文件读取对象
const rs = fs.createReadStream('hello.txt')
// 创建流式文件写入对象
const ws = fs.createWriteStream('hello2.txt')

// pipe()方法可以将可读流中的内容,直接输出到可写流中
rs.pipe(ws)

2.4 文件系统的其他常用方法

  • 判断文件是否存在

    • fs.existsSync(path) =》检查一个文件是否存在,返回布尔值
  • 获取文件状态

    • fs.stat(path,callback) =》获取文件的状态,会返回一个对象,这个对象中保存了当前对象状态相关信息
fs.stat('text.txt',function(err,stat){
	console.log(stat); // 一个对象
	console.log(stat.size); // 文件大小
	console.log(stat.isFile());
	console.log(stat.isDirectory);
	...
})
  • 删除文件

    • fs.unlink(path,callback);
    • fs.unlinkSync(path);
  • 列出文件目录结构

    • fs.readdir(path[,options],callback)
    • fs.readdirSync(path[,options[)
fs.readdir('.',function(err,files){
	if(!err){
		console.log(files); // files是一个字符串数组,每一个元素就是一个文件夹或文件的名字
	}
})
  • 截断文件

    • fs.truncate(path,len,callback)
    • fs.truncate(path,len)
      len是截取字节长度
  • 创建目录

    • fs.mkdir(path[,mode],callback)
    • fs.mkdirSync(path[,mode])
  • 删除目录

    • fs.rmdir(path,callback)
    • fs.rmdirSync(path)
  • 文件重命名

    • fs.rename(oldPath,newPath,callback)
    • fs.renameSync(oldPath,newPath)

同样可以作为剪切功能

  • 监听文件变化
    • fs.watchFile(filename[,options],listener)
      • 监视文件的修改
      • 参数
        filename 要监视的文件的名字
        options 配置选项
        listener 回调函数,当文件发生变化时,回调函数会执行(同样有两个参数,curr 当前文件的状态,prev 修改前文件的状态)

path模块

1、路径动态拼接的问题

问题描述

在使用fs模块操作文件时,如果提供的操作路径是以./或者…/开头的相对路径时,很容易出现路径拼接错误的问题。

原因

错误的原因是:代码在运行的时候,会以执行node命令所处的目录为基准,动态拼接出被操作文件的完整路径。

证明

假设在绝对路径C:\Users\Desktop\nodeTest\txt\下有一个test.txt文件,nodeTest下创建一个test.js文件,现在使用fs模块读取这个文件。

  • test.js
const fs = require('fs')

fs.readFile('./txt/test.txt','utf8',function(err,data){
    if(err){
        return console.log('文件读取失败! ' + err);
    }
    
    console.log('文件读取成功! ' + data)
})

这里设置的是相对路径,如果直接在test.js目录下使用命令node test.js是可以读取成功的。


但如果我们不是在test.js当前目录下,而是在当前目录的上一级目录C:\Users\Desktop\下呢?那我们需要node命令后面接上node nodeTest/test.js,但此时会发现报错:文件读取失败! Error: ENOENT: no such file or directory, open 'C:\Users\Desktop\txt\test.txt',因为设置的是相对路径,所以直接将要读取的相对于当前路径下了,而test.txt文件是在nodeTest/txt下的,这里直接略过了nodeTest目录。

解决

在使用fs模块操作文件时,直接提供完整的路径(使用绝对路径或者使用__dirname拼接,__dirname表示当前文件所处目录),不要提供相对路径,以此防止路径动态拼接。

const fs = require('fs')

fs.readFile(__dirname+'/txt/test.txt','utf8',function(err,data){
    if(err){
        return console.log('文件读取失败! ' + err);
    }
    
    console.log('文件读取成功! ' + data)
})

2、路径模块

path路径模块就是使用Node.js官方提供的、用来处理路径的模块。它提供了一系列的方法和属性,用来满足用户对路径的处理需求

例如:

  • path.join()方法,用来将多个路径片段拼接成一个完整的路径字符串
  • path.basename(path[,ext])方法,用来从路径字符串中,将文件名解析出来
    • path,表示一个路径的字符串,必选;
    • ext,表示文件扩展名,可选,
    • 返回值是路径中的最后一部分
  • path.extname(path)方法,返回扩展名

使用:

// 引入模块
const path = require('path')

// ================================= 
// - join的使用
// ..会抵消前面的路径,例如这里把c抵消掉了
// 并且使用join方法时,也同时会消除./d前面的'.'
const pathStr = path.join('/a','b/c','../','./d','e')

console.log(pathStr) // \a\b\d\e

// =================================
// -basename()的使用
const fpath = '/a/b/c/index.html'

var fullName = path.basename(fpath)
console.log(fullName); // index.html

var nameWithoutExt = path.basename(fpath,'.html')
console.log(nameWithoutExt) // index

// =================================
console.log(path.extname(fpath)); // .html

3、应用:html文件拆分

描述:
将html文档< script> < /script >,< style> < /style>标签中的内容抽离成单独的css文件和js文件,并且通过link标签引入对应的css和js文件

  • 一个完整的html页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .father{
            width: 300px;
            height: 300px;
            background-color: skyblue;
        }

        .son{
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>
<body>
    <div class="father">
        <div class="son">

        </div>
    </div>

    <script>
        window.onload = function(){
			var father = document.getElementsByClassName('father')[0];
	        father.onclick = function(){
	            alert('天蓝色')
	        }
	        var son = document.getElementsByClassName('son')[0]
	        son.onclick = function(event){
            alert('粉色')
            event.stopPropagation()// 阻止事件冒泡
        	}
		}
    </script>
</body>
</html>

在这里插入图片描述

  • 拆分具体步骤
    • 创建两个正则表达式,分别用来匹配< style> 和 < script> 标签
    • 使用fs模块,读取需要被处理的html文件
    • 自定义resolveCSS方法来写入main.css样式文件
    • 自定义resolveJS方法来写入main.js样式文件
    • 自定义resolveHTML方法来写入main.html样式文件
    • 拆分完之后,通过外联形式引入css和js文件

实现如下:
目录如下
在这里插入图片描述

  • separate.js实现拆分main.html功能
// 1】导入模块
// 导入fs模块
const fs = require('fs')
// 导入path模块
const path = require('path')

// 2】定义正则表达式,分别匹配style标签和script标签

const regStyle = /<style>[\s\S]*<\/style>/ // \s匹配任意空白字符,\S匹配任意非空白字符

const regScript = /<script>[\s\S]*<\/script>/

// 3】调用fs.readFile()方法读取文件
fs.readFile(path.join(__dirname,'./main.html'),'utf8',function(err,data){
    // 读取失败时
    if(err) return console.log('读取HTML文件失败!'+ err.message)

    // 读取成功时,调用对应的三个方法,分别拆解出css,js,html文件
    resolveCSS(data)
    resolveJS(data)
    resolveHTML(data)
})

// 处理css样式
function resolveCSS(htmlStr){
    // 使用正则提取页面中的style标签
    const r1 = regStyle.exec(htmlStr)

    // 将提取出来的样式字符串做进一步的处理:用空格替代标签style
    const newCSS = r1[0].replace('<style>','').replace('</style>','')

    // 将提取出来的css样式写入到main.css中
    fs.writeFile(path.join(__dirname,'../decoration/main.css'),newCSS,function(err){
        if(err) return console.log('写入CSS样式失败!' + err.message)
        console.log('写入CSS样式成功!');
    })
}

// 处理js样式
function resolveJS(htmlStr){
    // 使用正则提取页面中的style标签
    const r2 = regScript.exec(htmlStr)

    //将提取出来的js代码字符串做进一步的处理:用空格替代标签script
    const newJS = r2[0].replace('<script>','').replace('</script>','')

    // 将提取出来的js代码写入到main.js中
    fs.writeFile(path.join(__dirname,'../decoration/main.js'),newJS,function(err){
        if(err) return console.log('写入JS脚本失败!' + err.message)
        console.log('写入JS脚本成功');
    })
}

// 处理html文件
function resolveHTML(htmlStr){
    // 使用字符串的replace方法,把内嵌的style和script标签替换为外联引入的形式
    const newHTML = htmlStr
    .replace(regStyle,'<link rel="stylesheet" href="../decoration/main.css">')
    .replace(regScript,'<script src="../decoration/main.js"></script>')

    fs.writeFile(path.join(__dirname,'../decoration/main.html'),newHTML,function(err){
        if(err) return console.log("写入HTML文件失败!" + err.message);
        console.log("写入HTML页面成功!");
    })
}

结果如下:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值