Node.js篇02-path路径模块,file文件模块,加密,cookie和session(登录)

一.path路径模块

// 通过require引入  
const path = require('path')
//路径拼接 path.resolve(__dirname,'目录1','目录2', '待处理文件名)   __dirname: 当前目录
const filePath = path.resolve(__dirname,'./','./test1.txt') 

二.file文件模块

// 使用前先引入文件模块 
const fs = require('fs')
  1. 读取文件

(1)readFile(path, [option], callback)

异步方式,将要读取的文件内容完整读入缓存区, 再从缓存区读取文件的内容, 且没有返回值

(2) fs.readFileSync(path, [option])

同步读取数据,且读取的内容通过返回值获取, 若指定encoding选项,则该函数返回字符串, 否则返回buffer缓存
参数说明:

  • path: 文件路径 常使用path拼接路径

  • options: 对象, 包含encoding属性(字符编码)和flag文件系统标志, 未指定字符编码,返回原始buffer, 当为字符串时,只能表示字符编码

  • callback(err, data): 回调函数, err错误信息, data文件内容

(3)Promise实现readFile

// 引入模块
const fsPromise =  require('fs').promises  /  require('fs/promises').

(4)FileHandel包装器

FileHandle对象是数字文件描述符的包装器,由fsPromises.open()方法在内部创建

filehandle.readFile(encoding);执行语句返回值就是读取的内容

/* 1. 读取文件
    readFile(path, [option], callback)   异步方式,将要读取的文件内容完整读入缓存区, 再从缓存区读取文件的内容, 且没有返回值
    fs.readFileSync(path, [option]) 同步读取数据,且读取的内容通过返回值获取, 若指定encoding选项,则该函数返回字符串, 否则返回buffer缓存
    说明: 
      path: 文件路径 常使用path拼接路径  path.resolve(__dirname,'目录1','目录2', '待处理文件名)   __dirname: 当前目录
      options: 对象, 包含encoding属性(字符编码)和flag文件系统标志, 未指定字符编码,返回原始buffer, 当为字符串时,只能表示字符编码
      callback(err, data): 回调函数, err错误信息, data文件内容 
*/
const filePath = path.resolve(__dirname,'./','./test1.txt') 
// (1) readFile 异步读取   
const file1 = fs.readFile(filePath,'utf-8',(err,data)=>{
  console.log(' 1 异步读取readFile:',' err1:',err,' data1:',data);
})
console.log(' 2 file1: ',file1);

// (2) readFileSync 同步读取    无返回值
const file2 = fs.readFileSync(filePath,'utf-8')
const file3 = fs.readFileSync(filePath)    //未指定字符集
console.log(' 3 file2: ',file2); // i am okkk!!!
console.log(' 4 file3: ',file3); // <Buffer 69 20 61 6d 20 6f 6b 6b 6b 21 21 21>
console.log(' 5 file3: ',file3.toString()); // i am okkk!!!

// (3) Promise 实现readFile  可通过.then接收     需先引入模块require('fs').promises / require('fs/promises').
fsPromise.readFile(filePath).then(data=>{
  console.log(' 6 promise实现读取:',data.toString());
})
//  也可通过封装函数,将异步变为同步
const print = async ()=>{
 const data1 = await fsPromise.readFile(filePath)
 console.log(' 7 promise封装函数实现读取:',data1.toString())
 //  return 

 // (4) FileHandel实现  是一个Promise
  const fileHandel = await fsPromise.open(filePath)
  const data2 = await fileHandel.readFile()
  console.log(' 8 FileHandel实现读取:',data2.toString())
  return 
}
print()

/*  读取文件最终结果:  (打印顺序与同/异步有关)
    2 file1:  undefined
    3 file2:  i am okkk!!!
    4 file3:  <Buffer 69 20 61 6d 20 6f 6b 6b 6b 21 21 21>
    5 file3:  i am okkk!!!
    1 异步读取readFile:  err1: null  data1: i am okkk!!!
    6 promise实现读取: i am okkk!!!
    7 promise封装函数实现读取: i am okkk!!!
    8 FileHandel实现读取: i am okkk!!! 
*/
  1. 重写文件

writeFile(file, data, [options], callback);  写入文件 与读取相似, 默认新内容会覆盖原内容

/* writeFile(file, data, [options], callback);
      参数1:必选参数,文件存放路径
      参数2:必选参数,表示要写入的内容
      参数3:可选参数,写入文件内容格式,默认值是 utf-8
      参数4:必选参数,文件写入完成后的回调函数 
*/
const filePath = path.resolve(__dirname,'../logs','access.log');
const content =`${moment().format()}-${req.method}-${req.headers['user-agent']}:URL-${pathname}\n`;   // req.headers['user-agent'] 获取浏览器信息
await fsPromise.writeFile(filePath,content,{flag:'a'})

3.stream

文件IO性能慢, 使用stream流解决

(1)标准的输入输出

// (1) 标准的输入输出:   pipe()管道 连接 process.stdin 获取数据  (键盘) process.stdout 输出数据 (显示器)  
process.stdin.pipe(process.stdout)  //控制台输入,会显示输入的内容

(2)实现文件备份

// (2) 实现文件备份
// source 源
const source = path.resolve(__dirname,'../logs','test2-source.log')
// read stream 读取流
const rs = fs.createReadStream(source)
// dest 目标
const dest = path.resolve(__dirname,'../logs',`test2-dest${moment().format()}.log`)
const ws = fs.createWriteStream(dest,'utf-8')
// 连接
rs.pipe(ws)
rs.on('close',()=>{
  console.log('复制完成');
})
// 与日志, 定时任务结合可实现定时文件备份

(3)writeStream

const createWS = filename=>{
  const filePath = path.resolve(__dirname, '../05-日志(LOG)', filename);
  return fs.createWriteStream(filePath,{flags:'a',encoding:'utf-8'});
}
const content = `${moment().format()}-${req.method}-${req.headers['user-agent']}:URL-${pathname}\n`;
// 使用流的方式去写
createWS('test3.log').write(content);

(4)readline逐行读取

//需先安装并导入   npm i readline --save-dev
const readline = require('readline')

// (4)逐行读取文件 readline   先安装readline再导入
const source = path.resolve(__dirname, '../logs', 'access.log');
const rs = fs.createReadStream(source);
const rlInterface = readline.createInterface(rs);
let sum = 0;
let edgCount = 0;
rlInterface.on('line', data => {
  if (data.trim().length === 0) {
    return;
  }
sum += 1;
  if (data.indexOf('Edg') > -1) {
    edgCount += 1;
  }
});
rlInterface.on('close', () => {
  console.log('文件读取完成', edgCount / sum);
});
return { code: 0, message: `统计完成` }

三.加密

保存在数据库中的密码不能使用明文,必须要进行加密。

使用crypto, nodejs自身提供的库,直接引用

// 加密工具
const crypto = require('crypto')
// 使用md5进行加密
const secretMDPsd = password =>{
  // 创建hash对象并返回  
  /* crypto.createHash(algorithm) 创建并返回一个hash对象,它是一个指定算法的加密hash,用于生成hash摘要。 参数algorithm可选择系统上安装的OpenSSL版本所支持的算法。例如:'sha1', 'md5', 'sha256', 'sha512'等
     hash.update(data) 更新hash的内容为指定的data。当使用流数据时可能会多次调用该方法。
     hash.digest(encoding='binary')  计算所有传入数据的hash摘要。参数encoding(编码方式)可以为'hex', 'binary' 或者'base64' */
  const hash = crypto.createHash('md5')
  return hash.update(password).digest('hex')
}
// 使用Hmac加密   Hmac算法也是一种哈希算法,它可以利用MD5或SHA1等哈希算法。不同的是,Hmac还需要一个密钥
const  secretHMPsd = password =>{
    const secretKey = 'itkk--0521';
    const hmac = crypto.createHmac('md5', secretKey);
    return hmac.update(password).digest('hex')
}
module.exports = {
  secretMDPsd,
  secretHMPsd
}

四.cookie和session

实现登录功能时,与cookie和session相关

1.cookie和session

cookie 存储在浏览器端的字符串, 有大小限制, 4KB

  • 特点: 域的限制(不能跨域), 时间限制, 数量限制, 每个域下最多不超过50个cookie

  • 存储格式: k1=v1; k2=v2; k1=v1_afa(特殊格式)

  • 每次发送http请求, 会将请求域中的cookie一起发送给serve端, serve端可以查看cookie和修改cookie, 浏览器只读不可修改cookie

  • 使用cookie问题: 暴露用户名, 不安全

  • 解决办法: 在cookie中存放uuid, 在server服务器端保存用户信息, 让uuid与用户信息进行对应

2.查看cookie

方式1: 控制台network中

方式2: 通过js查看cookie: document.cookie(不推荐) (通过浏览器控制台输入)

3.实现登录功能

  • 客户端向浏览器每次发送http请求时,会携带cookie, 服务器端可以查看和修改cookie, 故用户登录操作可以将用户信息保存在cookie中, 下次登录时, 与浏览器中的cookie作对比, 如果有则不需要再登录, 如果没有则重新登录

 // 实现登录一次,无需多次登录   cookie保存在浏览器,会暴露用户信息不安全,解决办法: 用户信息保存在服务器端session中  cookie中保存一个令牌(uuid,一串字符),在session中保存令牌对应的用户信息
  // 服务器端处理cookie       客户端发送请求携带cookie, 将参数 服务器端判断是否有, 有则进行后续, 无则修改cookie并进行登录
  /* SESSION_DATA用于保存用户信息 , 后续将用户信息保存在req.session中
      登录操作时,
      cookie中是否有令牌
        ​	有令牌,则session无需设置, 
        ​		查看session中是否包含用户信息, 
        ​			无用户信息, 返回空数据{ }给客户端, 有令牌有用户信息, 
        ​			有用户信息, 则返回用户数据{,,,}

        ​	无令牌,需要设置cookie,随机生成令牌uuid,(由于首次登录,还没有用户信息,返回空用户信息)
        ​		生成后,将对应的用户信息保存在SESSION_DATA中返回给客户端 
  */
  // 1. 获取请求中的cookie字符串
  const cookiesStr = req.headers.cookie  //是个字符串
  // 2. 解析cookie(转换为对象)
  let cookies = {}
  if(cookiesStr){
    cookies = querystring.decode(cookiesStr,';','=')  //返回对象, 键值对
  }
  // 3. 将cookie保存在req中  由于cookie会暴露用户信息,故将用户信息保存在session中, 去除此步
  // req.cookies = cookies

  // 4. 获取cookies对象中对应的内容  声明cookie令牌uuid 
  let uuid = cookies['uuid'] || cookies[' uuid']   //浏览器cookie中从第二个开始,键前有个空格
  
  // 5. 声明一个变量 判断用户是否需要设置cookie, 用户登录信息不存在则需要设置
  let isWriteCookie = false
  // 判断session中是否包含cookie中令牌uuid对应的用户信息
  if(uuid){   //令牌存在
    if(!SESSION_DATA[uuid]){  //无用户信息
      SESSION_DATA[uuid] = {}    //存在令牌, 则不设置
    }
  }else{  //令牌不存在
    // 请求中无cookie, 后续服务器端设置cookie
    isWriteCookie = true  
    //首次登录不存在令牌,随机生成 uuid
    uuid = `${new Date().getTime()}_${Math.random(10000)+1}`
    SESSION_DATA[uuid] = {}  // 因为还未登录,故无用户信息
  }
  //用户信息保存在服务器端的session中
  req.session = SESSION_DATA[uuid]  
    // 对用户请求进行处理   ?作用: 匹配到,则进行后边.then, 否则不进行
    userRouter(req)?.then(data=>{
      // 6. 设置cookie
      if(data === undefined){
        return
      }
      // 判断是否需要设置cookie
      if(isWriteCookie){  //true
        // 设置cookie  'Set-Cookie','key=value;httpOnly;path=/;httpOnly;expiress=时间'
        res.setHeader('Set-Cookie',`uuid=${uuid};httpOnly;path=/;httpOnly;expiress=${getExpiresTime()}`)
      }
      SESSION_DATA[uuid] = data.data
      res.session = SESSION_DATA[uuid]
      res.end(JSON.stringify(data))
      return
    })

PreviousNotes:

https://blog.csdn.net/qq_54379580/article/details/126781836?spm=1001.2014.3001.5501

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mteee.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值