node内置模块和服务器开发

global

浏览器中全局对象window

node 中的全局对象 global

process

process进程管理相关

// 1.node命令行参数
// node test2.js 1 2
// [
//   'C:\\Program Files\\nodejs\\node.exe',
//   'D:\\vue_project\\learn_node\\test2.js',
//   '1',
//   '2'
// ]
console.log(process.argv);


// 2. 用户环境
// 设置和查看当前环境
process.env.NODE_ENV = 'dev'
console.log(process.env.NODE_ENV);


// 3.当前进程的工作目录
console.log(process.cwd());

// 4.屏幕输入输出
process.stdout.write("输入文字:")
// process监听data事件
process.stdin.on("data", (res) => {
  console.log(res.toString());

  // 5.退出当前进程
  process.exit()
})


// 5.内存使用情况
console.log(process.memoryUsage());

util

util.callbackify()

将返回值为promise的函数改为回调

const util = require('util')

function foo() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(666)
    }, 500)
  })
}

// 将返回值为promise的函数改为回调
const callbackFoo = util.callbackify(foo)

// node中所有异步操作都是通过回调函数获取结果
// 回调函数的第一个参数err,第二个参数res
callbackFoo(function (err, res) {
  console.log(res);
})
// 打印:666

util.promisify()

将异步回调的函数改为promise

const util = require('util')
const fs = require('fs')

// node中所有异步操作都是通过回调函数获取结果
// 回调函数的第一个参数err
// fs.readFile('./a.txt',(err,res)=>{
//   console.log(res);
// })

// 将异步的回调函数转化为promise形式
const promiseReadFile = util.promisify(fs.readFile)
promiseReadFile('./a.txt').then(res => {
  console.log(res.toString());
})

fs

文件操作

  • 文件方法都为异步操作,都有对应的同步版本,函数名+Sync
  • 异步函数的回调函数,第一个参数为err,第二个参数为data
  • 都有fs.promises.xxx形式
const fs = require('fs')

// 1.写入
fs.writeFileSync('./a.txt', '123')

// 2.追加
fs.appendFileSync('./a.txt', '\n456')

// 3.读取
fs.readFile('./a.txt', (err, res) => {
  console.log(res.toString());
})

// 4.剪切
fs.rename('./a.txt', './dir/a.txt', (err) => { })
// 重名名
// fs.rename('./dir/a.txt', './dir/aaaa.txt', (err) => { })

// 5.拷贝
fs.copyFileSync('./test.js', './test_copy.js')

// 6.删除
fs.rm("./a_copy2.txt", (err) => {});

文件夹操作

  • 文件方法都为异步操作,都有对应的同步版本,函数名+Sync
  • 异步函数的回调函数,第一个参数为err,第二个参数为data
  • 都有fs.promises.xxx形式
const fs = require('fs')

// 1.读取
fs.readdir('./', (err, res) => {
  console.log(res);
})

// 2.创建文件夹
fs.mkdir("./B/BB/b.txt", { recursive: true }, (err) => {});
fs.mkdirSync("./C/CC/c.txt", { recursive: true });


// 3.删除空文件夹,不能删除有内容的文件夹
fs.rmSync("./A/AA", { recursive: true });

// 4.文件或文件夹信息
// 判断文件或文件夹
const state = fs.statSync('./A/a.txt')
console.log(state);
console.log(state.isDirectory());
console.log(state.isFile());

// 5.文件或文件夹是否存在
console.log(fs.existsSync('./A/b.txt'));

递归遍历文件夹

function handleDirection(path) {
  fs.readdir(path, { withFileTypes: true }, (err, files) => {
    if (err) {
      console.log(err);
      return;
    }

    files.forEach((item) => {
      if (item.isDirectory()) {
        console.log("文件夹:", item.name);
        handleDirection(`${path}/${item.name}`);
      } else {
        console.log("文件:", item.name);
      }
    });
  });
}

handleDirection("./A");

案例1:清除文件夹中所有的文件

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


function clearDir(dirPath) {

  // 文件夹存在
  if (fs.existsSync(dirPath)) {

    // 获取文件夹信息
    const dirArray = fs.readdirSync(dirPath)

    dirArray.forEach((item) => {
      // 拼接路径,判断是文件还是文件夹
      let fullPath = path.join(dirPath, item)
      const state = fs.statSync(fullPath)
      if (state.isDirectory()) {
        clearDir(fullPath)
      } else {
        fs.unlinkSync(fullPath)
      }
    })

  }
}

clearDir('./A')

案例2:处理数据后写入新文件

const fs = require('fs')

// 1.读文件
fs.readFile(path.join(__dirname,'成绩.txt'), 'utf8', function (err, dataStr) {
    if (err) throw err
	
    // 处理数据
    let data = dataStr.replaceAll('=', ':').split(' ').join('\n')

    console.log(data);

    // 2.写文件
    fs.writeFile('./成绩2.txt', data, (err) => {
        if (err) throw err;
        console.log('The file has been saved!');
    });
})



//promise方式
//fs.promises.readfile()

成绩1.txt

小红=99 小白=100 小黄=70 小黑=66 小绿=88

成绩2.txt

小红:99
小白:100
小黄:70
小黑:66
小绿:88

案例3 封装readDir函数,能递归遍历所有文件夹,每个文件/文件夹包装为一个对象

const fs = require("fs");
const path = require("path");

class File {
  constructor(filename, name, ext, isFile, size, createTime, updateTime) {
    this.filename = filename;
    this.name = name;
    this.ext = ext;
    this.isFile = isFile;
    this.size = size;
    this.createTime = createTime;
    this.updateTime = updateTime;
  }

  // 读取文件内容
  async getContent(isBuffer = false) {
    if (this.isFile) {
      if (isBuffer) {
        return await fs.promises.readFile(this.filename);
      } else {
        return await fs.promises.readFile(this.filename, "utf-8");
      }
    }
    return null;
  }

  // 文件夹的所有子文件对象
  async getChildren() {
    if (this.isFile) {
      return [];
    }
    let children = await fs.promises.readdir(this.filename);
    children = children.map(name => {
      const result = path.resolve(this.filename, name);
      return File.getFile(result);
    });
    return Promise.all(children);
  }

  // 每个文件/文件夹 返回为一个对象
  static async getFile(filename) {
    const stat = await fs.promises.stat(filename);
    const name = path.basename(filename);
    const ext = path.extname(filename);
    const isFile = stat.isFile();
    const size = stat.size;
    const createTime = new Date(stat.birthtime);
    const updateTime = new Date(stat.mtime);
    return new File(filename, name, ext, isFile, size, createTime, updateTime);
  }
}

async function readDir(dirname) {
  const file = await File.getFile(dirname);
  return await file.getChildren();
}

module.exports = {
  readDir
}

fs-extra

fs增强版,能处理文件、文件夹的增删改查

npm i fs-extra

compressing

npm i compressing
const fs = require('fs-extra')
const path = require('path')
const compressing = require('compressing')

// 压缩文件夹
compressing.zip.compressDir(path.join(__dirname, './A.zip'), './A.zip').then((res) => {
  console.log('压缩完成');
})

// 解压
compressing.zip.uncompress(path.join(__dirname, './A.zip'), './A2').then(res => {
  console.log('解压完成');
}).catch(err => {
  console.log(err);
})

path

node中文件的相对路径与绝对路径

相对路径:相对于命令行的工作目录
绝对路径(__dirname):当前文件所在目录的绝对路径在这里插入图片描述

URL的相对路径和绝对路径

绝对路径:
在这里插入图片描述
相对路径:
在这里插入图片描述

路径拼接

const path = require('path')
const pathStr1 = path.join('/a','/b/c','../','./d')
console.log(pathStr1);
//结果:\a\b\d

//拼接为绝对路径,从右向左拼接,拼接出绝对路径为止
const pathStr2 = path.resolve()
console.log(pathStr2);
//结果:D:\vue_project\learn_webpack

const pathStr3 = path.resolve('/a','./b/c','../')
console.log(pathStr3);
//结果:D:\a\b

const pathStr4 = path.resolve('./a','./b/c','../')
console.log(pathStr4);
//结果:D:\vue_project\learn_webpack\a\b

__dirname 当前所在文件的目录的绝对路径

__filename 当前所在文件的绝对路径

解析路径

let path = require('path')

let demo = "./src/a/b/c/test.js"

// 1.文件名
// test.js
console.log(path.basename(demo));

// 2.目录名
// ./src/a/b/c
console.log(path.dirname(demo));

// 3.将路径解析为对象
// {
//   root: '',
//   dir: './src/a/b/c',
//   base: 'test.js',
//   ext: '.js',
//   name: 'test'
// }
console.log(path.parse(demo));

// 4.将对象解析为路径
// ./src/a/b/c\test.js
console.log(path.format({
  root: '',
  dir: './src/a/b/c',
  base: 'test.js',
  ext: '.js',
  name: 'test'
}));

总结

在这里插入图片描述

events

events全局事件总线

const EventEmitter = require("events");
const emitter = new EventEmitter();

// 1. 监听e1事件,接收参数
// emitter.on("e1", (name,age) => {
//   console.log(name);
//   console.log(age);
// });

function listenerE1(name,age){
  console.log(name);
  console.log(age);
}
emitter.on("e1", listenerE1);

// 2. 抛出事件,传递参数
emitter.emit("e1", "CCC", 999);

// 3. 取消事件监听
emitter.off('e1',listenerE1)

Buffer

将数据保存为二进制,用16进制表示

buffer相当于固定长度的字节数组,每一项为一个字节

buffer可以使用字符串的方法

//创建buffer(编码)
const bf2 = Buffer.from("hello")
//创建3字节的空间
const bf1 = Buffer.alloc(10,'1')

//打印buffer结果
//<Buffer 00 00 00>
console.log(bf1);

//buffer转原始数据(解码)
//1111111111
console.log(bf1.toString());

//true
console.log(Buffer.isBuffer(bf1));

stream

概念

流:表现为连续的字节
在这里插入图片描述
所有的流都是EventEmitter的实例,故流都能事件监听

流的类型:
在这里插入图片描述

可读流

const fs = require('fs')

// 创建可读流
const readStream = fs.createReadStream('./a.txt', {
  // 下标0开始 [3,4]之间的buffer数组
  start: 3,
  end: 4,
  // 一次性读取5个字节
  highWaterMark: 5
})

// 监听data事件(因为流是EventEmitter的实例)
// 每读5个字节,回调一次
readStream.on('data', (res) => {
  console.log(res.toString());

  // 暂停
  readStream.pause()

  // 2s后继续
  setTimeout(() => {
    readStream.resume()
  }, 2000);
})

监听其他事件

曹操123abc
const fs = require("fs");

// 创建可读流
const readStream = fs.createReadStream("./a.txt", {
  // 下标0开始 [3,4]之间的buffer数组
  start: 6,
  end: 8,
  // 一次性读取2个字节
  highWaterMark: 2,
});

readStream.on("open", (fd) => {
  console.log("文件描述符:", fd);
});

// 监听data事件(因为流是EventEmitter的实例)
// 每读2个字节,回调一次
readStream.on("data", (res) => {
  console.log(res.toString());
});

readStream.on("end", () => {
  console.log("读取完成");
});

readStream.on("close", () => {
  console.log("文件关闭");
});

//打印结果:
文件描述符: 3
12      
3       
读取完成
文件关闭

可写流

const fs = require('fs')

const writeStream = fs.createWriteStream('./a.txt',{
  flags: 'r+',
  start: 6
})

writeStream.write('1')
writeStream.write('2',()=>{
  console.log('2写入完成');
})

// 将'end!!'写入完成后,自动关闭关闭文件
// 手动关闭文件 writeStream.close()
writeStream.end('end!!')

// 监听文件关闭
// 注:close能监听到手动关闭的文件
writeStream.on('close',()=>{
  console.log('close:文件关闭');
})

writeStream.on('finish',()=>{
  console.log('finish:文件关闭');
})

pipe管道

pipe能将可读流和可写流拼接

案例:文件复制

方式1 一次性读写

const data = fs.readFileSync('./a.txt')
fs.writeFileSync('./a_copy2.txt',data)

方式2 可读可写流

const fs = require('fs')

const readStream = fs.createReadStream('./a.txt')
const writeStream = fs.createWriteStream('./a_copy.txt')

readStream.on('data',(data)=>{
  writeStream.write(data)
})

readStream.on('end',()=>{
  console.log('读取文件a.txt完成');
  // 手动关闭a_copy.txt
  writeStream.close()
})

方式3 pipe函数

  • 可解决背压问题
// 实现文件复制
const fs = require('fs')

const readStream = fs.createReadStream('./a.txt')
const writeStream = fs.createWriteStream('./a_copy.txt')

// 从可读流流向可写流
readStream.pipe(writeStream)

网络协议

网络协议共同特点;

  1. 都有客户端和服务端
  2. 大多数操作以流的形式

http

IP分类

在这里插入图片描述

端口被占用

  1. 打开资源监视器,找到端口对应的PID
    在这里插入图片描述
  2. 打开任务管理器,根据刚刚的PID结束对应的进程
    在这里插入图片描述

请求报文

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
获取请求报文
在这里插入图片描述

响应报文

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
响应状态码
在这里插入图片描述

服务端

服务端:根据不同网址显示不同页面内容

  • 服务端接收到请求req,故req是可读流

  • 服务端返回响应res,故res是可写流

1.根据不同路径显示不同页面

const http = require('http')
const url = require('url')
const fs = require('fs')

const serve = http.createServer()

// 服务端中,req为可读流,res为写入流
serve.on("request", (req, res) => {
  // 解析url
  const urlObj = url.parse(req.url, true)

  // 根据不同网址显示不同页面
  if (urlObj.pathname === '/page1') {
    res.write("hi ")
    res.end("page1")
  }

  if (urlObj.pathname === '/page2') {
    res.write("hi ")
    res.end("page2")
  }

  if (urlObj.pathname === '/htmlPage') {
    // 创建可读流,避免html文件过大,影响响应速度
    const _html = fs.createReadStream('./test.html')
    _html.on('data', (chunk) => {
      res.write(chunk)
    })
    _html.on('end', () => {
      res.end()
    })
  }

  if (urlObj.pathname === '/test.css') {
    // 创建可读流,避免css文件过大,影响响应速度
    const _css = fs.createReadStream('./test.css')
    _css.on('data', (chunk) => {
      res.write(chunk)
    })
    _css.on('end', () => {
      res.end()
    })
  }
})

// 主机host默认为 0.0.0.0 能监听IPV4所有地址
// 127.0.0.1(localhost) 本地回环地址 
serve.listen(3000)

2.根据不同请求方法、路径,返回不同数据(即api接口)

const http = require("http");
const url = require("url");

const server = http.createServer((req, res) => {
  // 解析URL
  const urlObj = url.parse(req.url, true);

  // 1.get的api接口
  if (urlObj.pathname === "/api1" && req.method === "GET") {
    // 将解析出来的query参数返回
    res.end(
      JSON.stringify({
        data: [1, 2, 3],
        user: urlObj.query,
      })
    );
  }

  // 2.post的api接口
  else if (urlObj.pathname === "/api2" && req.method === "POST") {
    // post携带的参数在body中,通过流读取
    let postParam = "";
    req.on("data", (chunk) => {
      postParam += chunk;
    });
    req.on("end", () => {
      res.end(
        JSON.stringify({
          data: [4, 5, 6],
          postParam: JSON.parse(postParam),
        })
      );
    });
  } else {
    res.statusCode = 400;
    res.end(
      JSON.stringify({
        msg: "400 客户端错误",
      })
    );
  }
});

server.listen(3000, "localhost", () => {
  console.log("服务器地址:", "http://localhost:3000");
}); 

客户端

客户端:客户端请求服务端接口

  • 客户端接收到响应res,故res是可读流
  • 客户端请求服务器,故req是可写流
const http = require("http");

// 1.客户端发送get请求
http.get("http://localhost:3000/api1?name=cjc&age=100", (res) => {
  let data = "";
  res.on("data", (chunk) => {
    data += chunk;
  });

  res.on("end", () => {
    console.log(JSON.parse(data));
  });
});

// 2.客户端发起post请求
const req = http.request(
  {
    method: "POST",
    hostname: "localhost",
    port: "3000",
    path: "/api2",
  },
  (res) => {
    let data = "";
    res.on("data", (chunk) => {
      data += chunk;
    });

    res.on("end", () => {
      console.log(JSON.parse(data));
    });
  }
);

// post请求,请求体body中携带参数
req.write(
  JSON.stringify({
    a: 1,
    b: 2,
    c: 3,
  })
);

req.end();

使用axios发送请求

const { default: axios } = require("axios");

axios
  .get("http://localhost:3000/api1?name=cjc&age=100")
  .then((res) => {
    console.log(res.data);
  })
  .catch((err) => {
    console.log(err);
  });

axios
  .post("http://localhost:3000/api2", JSON.stringify({a:1,b:2,c:3}))
  .then((res) => {
    console.log(res.data);
  })
  .catch((err) => {
    console.log(err);
  });

postman工具

浏览器请求一个网址时,会发送两次请求,为避免干扰,可以使用postman工具
在这里插入图片描述

https

https对比于http服务,多了加密的过程

服务器传来的通过公钥加密,本地客户端有私钥,用来解密

生成本地证书

  • 生成的本地证书,不会被信任
npm i -g mkcert

// 查看安装完成
mkcert --help

// 生成相关证书
mkcert create-ca
mkcert create-cert

服务端

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

const options = {
  // 私钥
  key: fs.readFileSync(path.resolve(__dirname, "./cert.key")),

  // 由私钥生成的证书
  cert: fs.readFileSync(path.resolve(__dirname, "./cert.crt"))
}

const server = https.createServer(options, (req, res) => {
  res.end("https server")
})

server.listen(4000,()=>{
  console.log('https://localhost:4000');
})

websocket

npm i ws -S

服务端

const ws = require('ws')
const webSocketServer = ws.Server
const wss = new webSocketServer({ port: 4000 })
wss.on('connection', (wsconnect) => {
  wsconnect.on('message', (msg, err) => {

    // 在终端中打印信息
    console.log('Server: 接收到客户端的消息'+msg);

    // 响应给客户端的信息
    wsconnect.send("Server: hi", () => { })
  })
})

客户端

  • 以浏览器为例
<!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>
</head>

<body>

  <script>

    const ws = new WebSocket("ws://localhost:4000/")
    ws.onopen = function () {
      console.log('connect');
    }

    ws.onmessage = function (e) {
      console.log(e.data);
    }
  </script>
</body>

</html>

客户端发送消息给服务器:

ws.send("test")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值