一、fs文件系统模块

1.什么是文件系统模块

fs是Node.js官方提供的,用来操作文件的模块,它提供了一系列的方法和属性用来满足用户对文件的操作。

  • fs.readFile() 读取指定文件的内容
  • fs.writeFile() 向指定文件中写入内容

在js中如果需要fs来操作文件需要先引入:

const fs=require('fs')

‘fs’在我们安装node的时候就已经到电脑本地了

2.使用fs.readFile()来读取文件

fs.readFile(path[,options],callback)用[ ]括起来的就是可选参数,options默认值都是utf8

path路径,options以什么编码格式来读取文件(比如‘utf8’),callback通过回调函数拿到读取的结果

小🌰:

const fs=require('fs')

fs.readFile('./file/1.txt','utf8',function(error,data){
console.log(error,'error')
console.log('888')
console.log(data,'data')
})

文件结构如图:

终端输出:

读取结果:

读取成功的时候,error的值为null

读取失败的时候,dataStr的值为undefined

验证文件读取失败:看error返回的是否是null

3.使用fs.write()来写文件

fs.writeFile(path,data[,options],callback)

写入成功的话error是null,写入失败的话error是一个错误对象

fs.writeFile('./file/1.txt','你好呀','utf8',function(error){
console.log(error,'error')

})

这个回调函数没有data,error是null再去看文件里已经被写成功了

还可以使用这个来新建文件,运行下面这段之后可以看到file文件夹下面多了一个2.txt文件

const fs=require('fs')

fs.writeFile('./file/2.txt','哈哈哈2','utf8',function(error){
if(error===null){
  return console.log('写入成功了')
}
console.log('写入失败了')
})

4.练习:将文件1的成绩写入到文件2中

先创建一个成绩文件1.txt,然后开写

const fs=require('fs')

fs.readFile('./file/1.txt','utf8',function(error,data){
if(error===null){
  console.log('读到了数据:',data)
  fs.writeFile('./file/2.txt',data,'utf8',function(error2){
    if(error2===null){
      console.log('写入成功了!!')
    }
  })
}

})

 easy!!

5.fs路径动态拼接

在fs读写操作中,如果用./或者../这种很容易有拼接错误的问题

因为它不是先找到自己当前所在的路径然后再node xxx.js去运行的吗,实际上是用当前目录拼接上那个相对路径合成的,所以如果你在执行命令时所处的文件位置不对那就出错了

比如说我们之前的路径是 a/b/c/d下运行 node .\test.js,现在你改成a/b/c下运行 node d/test.js就会报错,因为它执行到那个文件看到的是./file/1.txt,那它就去找a/b/c下的./file/1.txt了

解决方法:

(1)用全路径

直接提供一个完整的路径,从盘符写到真正位置,直接在文件右键复制路径,以我复制的为例:

(mac)/Users/xxx/Desktop/my-backend-project/test.js

注意js中‘/’代表转移的意思,还需要补全,将‘/’写为‘//’就是一个真正的斜线了

这里mac跟windows不太一样,mac不写//也可以,写了也可以;但是node后面的路径直接用xx/yy就行,用.\xx\yy会报错

(2)__dirname

上面这种的话移植性非常差,如果将来路径改了之后前面那一大串都得重写。

那么node为我们提供了一个 成员__dirname,表示当前这个文件所处的目录

fs.readFile(__dirname+'/file/1.txt','utf8',function(error,data){
if(error===null){
  console.log('读到了数据:',data)
}
})

二、path路径模块

1.什么是路径模块

path模块是node提供的用于处理路径的模块,用来满足用户对路径处理的需求。

  • path.join(),用来将多个路径片段拼成一个完整的字符串
  • path.basename(),从路径字符串中将文件名解析出来
  • path.extname(),获取路径中的文件扩展名

引入方式:

const path=require('path')

2.path.join()方法

path.join([...paths]),参数个数没有要求 (我还以为是数组形式,结果是可选)

const path=require('path')
const str=path.join('/a','b/c','../','/e')
console.log(str,'str')

../会消除上一级路径,./没啥作用

然后还可以用__dirname,这样的话不用+了,用+可能会有问题,比如说__dirname+'./file/1.txt'就不对,但是用path.join(__dirname,'./file/1.txt')就没问题,会自己屏蔽掉.

path.join(__dirname,'file/1.txt')
输出:
/Users/xxx/Desktop/my-backend-project/file/1.txt str

3.path.basename()方法

path.basename(path[,ext]) ext是文件扩展名,这个方法会把路径的最后一部分内容返回回来

const path=require('path')
const str=path.basename('a/b/c/1.txt')
console.log(str,'str')

如果传入扩展名的话:const str=path.basename('a/b/c/1.txt','.html') 就会只得到文件名 1

4.path.extname()方法

path.extname(path)

const path=require('path')
const str=path.extname('a/b/c/1.txt')
console.log(str,'str')

三、时钟案例

将index-1.html里的(如下:)css、js和去除css和js的html(只引入,无代码)三个文件分别拆解出来

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>index首页</title>
  <style>
    html,
    body {
      margin: 0;
      padding: 0;
      height: 100%;
      background-image: linear-gradient(to bottom right, red, gold);
    }

    .box {
      width: 400px;
      height: 250px;
      background-color: rgba(255, 255, 255, 0.6);
      border-radius: 6px;
      position: absolute;
      left: 50%;
      top: 40%;
      transform: translate(-50%, -50%);
      box-shadow: 1px 1px 10px #fff;
      text-shadow: 0px 1px 30px white;

      display: flex;
      justify-content: space-around;
      align-items: center;
      font-size: 70px;
      user-select: none;
      padding: 0 20px;

      /* 盒子投影 */
      -webkit-box-reflect: below 0px -webkit-gradient(linear, left top, left bottom, from(transparent), color-stop(0%, transparent), to(rgba(250, 250, 250, .2)));
    }
  </style>
</head>

<body>
  <div class="box">
    <div id="HH">00</div>
    <div>:</div>
    <div id="mm">00</div>
    <div>:</div>
    <div id="ss">00</div>
  </div>

  <script>
    window.onload = function () {
      // 定时器,每隔 1 秒执行 1 次
      setInterval(() => {
        var dt = new Date()
        var HH = dt.getHours()
        var mm = dt.getMinutes()
        var ss = dt.getSeconds()

        // 为页面上的元素赋值
        document.querySelector('#HH').innerHTML = padZero(HH)
        document.querySelector('#mm').innerHTML = padZero(mm)
        document.querySelector('#ss').innerHTML = padZero(ss)
      }, 1000)
    }

    // 补零函数
    function padZero(n) {
      return n > 9 ? n : '0' + n
    }
  </script>
</body>

</html>

直接上代码:(正则咱也不太会,每次遇见正则都是直接chat)

/<\/?style\b[^>]*>/gi

/是开始<

\/是为了不让/识别为转义,所以\/就是/

?表示的是前面的那个数量,也就是/ 0或1次,识别出来的是<style和</style

\b是单词边界,也就是style得是一个完整的单词 <styler 这种就不行

[^>]*匹配不是>的字符,多少都行

然后>

G是全局匹配,而不是找到第一个就停止

I表示不区分大小写

但是这个只能识别出<style></style>拿出那一堆还得截取字符串

/<style\b[^>]*>[\s\S]*?<\/style>/gi这个的话就是没有了\/,直接取出样式的那一堆代码

const fs=require('fs')
const path=require('path')

// console.log(path.join(__dirname,'/index-1.html'))

fs.readFile(path.join(__dirname,'/index-1.html'),'utf8',function(error,data){
  if(error===null){
     // 1. 提取所有 <style> 内容
    const styleRegex = /<style\b[^>]*>[\s\S]*?<\/style>/gi;
    const styleMatches = data.match(styleRegex);
    const styleContent = styleMatches 
      ? styleMatches.map(item => item.replace(/<\/?style\b[^>]*>/gi, '')).join('\n') 
      : '';

    // 2. 提取所有 <script> 内容
    const scriptRegex = /<script\b[^>]*>[\s\S]*?<\/script>/gi;
    const scriptMatches = data.match(scriptRegex);
    const scriptContent = scriptMatches
      ? scriptMatches.map(item => item.replace(/<\/?script\b[^>]*>/gi, '')).join('\n')
      : '';

    // 结果在这里!!!
    console.log('===== 提取到的 CSS 样式 =====');
    console.log(styleContent);

    console.log('\n===== 提取到的 JS 脚本 =====');
    console.log(scriptContent);
    fs.writeFile(path.join(__dirname,'/index.css'),styleContent,function(error){
      if(error===null){
        console.log('css文件写入成功!')
      }
    })
    fs.writeFile(path.join(__dirname,'/index.js'),scriptContent,function(error){
      if(error===null){
        console.log('js文件写入成功!')
      }
    })
    let result = data.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, '');

    // 2. 删掉所有 <script>...</script>
    result = result.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, '');

    // 3. 插入外部 CSS(放在 </head> 前面)
    result = result.replace('</head>', `  <link rel="stylesheet" href="./index.css">\n</head>`);

    // 4. 插入外部 JS(放在 </body> 前面)
    result = result.replace('</body>', `  <script src="./index.js"></script>\n</body>`);

    // 输出最终处理好的 HTML
    console.log('===== 处理后的 HTML =====');
    console.log(result);

    fs.writeFile(path.join(__dirname,'/index.html'),result,function(error){
      if(error===null){
        console.log('html文件写入成功!')
      }
    })
  }
})

// fs.readFile(__dirname)

注意点:

(1)fs.writeFile()只能创建文件,不能创建路径(不能自动创建文件夹)

(2)重复调用fs.writeFile()去写同一个文件,新写入的内容会覆盖之前的旧内容

四、http模块

1.什么是http模块

        在网络节点中,负责消费资源的电脑,叫客户端;负责对外提供网络资源的电脑,叫服务器;http模块就是node提供用来创建web服务器的模块,通过http提供的http.createServer()就可以把一台普通的电脑,变成可以向外提供Web资源的服务器。

导入方式:

const http=require('http')

服务器与普通电脑的区别就是服务器上安装了web服务器软件,比如IIS、apache

比如当我们安装apache之后(电脑就有程序在监听你的127.0.0.1 80端口(默认)了),在根目录下放一个html文件,然后在浏览器输入127.0.0.1就可以看到这个页面,不过别人看不到这个网址

IP地址

是每个人电脑的唯一地址,IP地址具有唯一性

在自己电脑输入127.0.0.1,就能把自己电脑当作一台服务器进行访问了,127.0.0.1的域名对应的是localhost

域名就是代替IP地址的一个方便记忆的字符串型的地址方案(IP和域名是一一对应的关系,这个关系存放在域名服务器(DNS中))

端口号

计算机中的端口号类似于大楼的门牌号,比如我们平时运行多个项目,他们都运行在不同的端口号中,客户端发送过来的网络请求通过端口号交给对应的web服务。

注意:(1)一个端口号不能被多个web服务占用

(2)80端口在实际应用中可以被省略

2.创建最基本的web服务器

(1)导入http模块

const http = require('http')

(2)创建web服务器实例

http.createServer()

(3)为服务器实例绑定requst请求,监听客户端的请求

使用服务器实例的.on方法,绑定一个request事件处理函数

server.on('request',(req,res)=>{
  console.log('77')
})

当有客户端请求这个服务就会触发这个回调函数,然后输出77

(4)启动服务器

调用服务器的.listen()方法就可以启动当前的web服务器实例

server.listen('80',()=>{
  console.log('服务器启动成功')
})

然后依旧在终端去运行,运行完之后终端输出:(光标还在一直闪,在监听)

当我们在浏览器输入127.0.0.1:80,发现终端输出:

而且浏览器这时候会一直在转圈,这是因为我们没有给客户端任何响应

问题:listen为什么是启动用的,他不应该是监听吗?

listen监听=启动+等待请求(开启后只听不动),第一个参数写多少就只启动哪个端口,其他的端口不启动

server.listen('80'就好比在80房间号开门等你,浏览器进入127.0.0.1:80(127.0.0.1)就等于进了80房间,如果访问127.0.0.1:8080就会报错,因为8080没开门

监听=服务器开门=启动成功

request=收到请求之后做什么

3.req请求对象

服务端接收到客户端的请求之后就会调用server.on()为服务器绑定的request的事件处理函数

(node底部感知请求自动触发request事件)

req是回调函数中的请求对象,可以用来访问客户端的数据或属性

req.url是请求路径,req.method是请求方法(get/post、、、)

const http=require('http')

const server=http.createServer()

server.on('request',(req)=>{
  console.log(req.url,'url')
  console.log(req.method,'method')

})

server.listen('80',()=>{
  console.log('启动')
})

浏览器默认只能发起get请求,如果想发起post请求的话可以用postman

4.res响应对象

在服务器的request

res.end向客户端发送指定内容,并且结束此次请求

之前我们客户端的请求服务端就只是监听没有发送任何结果,用end可以去发送

const http=require('http')

const server=http.createServer()

server.on('request',(req,res)=>{
  res.end('哈哈哈')
})

server.listen('80',()=>{
  console.log('启动')
})

然后启动之后我发现浏览器访问得到的是乱码

乱码解决:

(1)问了ai之后发现在发送之前加这么一句就好了(英文没问题):

    res.setHeader('Content-Type','text/plain; charset=utf-8')

(2)或者是这样:

res.setHeader('Content-Type','text/html; charset=utf-8')
res.end('<h1>你好哈哈</h1>')

注意:箭头函数如果要加上res的话必须得写两个(req,res),req的话写他自己就行

顺序不能乱,node是按顺序传参

问题:为什么修改代码之后需要重启服务器

因为node的运行代码是一次性读取代码到内存之后就不再看文件了

硬盘的代码修改了但是内存里运行的还是老代码

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐