path
path模块在不同的操作系统是有差异的(windows | posix)
posix(Portable Operating System Interface of UNIX)
posix表示可移植操作系统接口,也就是定义了一套标准,遵守这套标准的操作系统有(unix,like unix,linux,macOs,windows wsl),为什么要定义这套标准,比如在Linux系统启动一个进程需要调用fork函数,在windows启动一个进程需要调用creatprocess函数,这样就会有问题,比如我在linux写好了代码,需要移植到windows发现函数不统一,posix标准的出现就是为了解决这个问题。
Windows 并没有完全遵循 POSIX 标准。Windows 在设计上采用了不同于 POSIX 的路径表示方法。
在 Windows 系统中,路径使用反斜杠(\)作为路径分隔符。这与 POSIX 系统使用的正斜杠(/)是不同的。这是 Windows 系统的历史原因所致,早期的 Windows 操作系统采用了不同的设计选择。
windows--\ posiz--/
basename返回给定路径的最后一部分
posix 处理不了windows的路径
js
const path = require('node:path')
// 1、basename返回给定路径的最后一部分
console.log(path.basename('C:/temp/myfile.html'))// myfile.html
console.log(path.basename('C:\\temp\\myfile.html'))// myfile.html
console.log(path.basename('/foo/bar/baz/myfile.html'))// myfile.html
// 我们现在是windows系统,默认是C:\CN\全栈AI\node\nodecode>
// window兼容正斜杠的写法
console.log(path.basename('\\foo\\bar\\baz\\myfile.html')) //myfile.html
// 但是posix 处理不了windows的路径
// 在windows的环境下,模拟posix去处理这个路径
console.log(path.posix.basename('\\foo\\bar\\baz\\myfile.html')) //\foo\bar\baz\myfile.html
// 比如现在是macOs,给我处理windows路径
console.log(path.win32.basename('\\foo\\bar\\baz\\myfile.html')) //myfile.html
dirname 返回路径的目录名
js
console.log(path.dirname('\\foo\\bar\\baz\\myfile.html')) //\foo\bar\baz
extname 返回路径的扩展名 egg: .html .js
join 拼接路径
js
console.log(path.join('/a','/b','/c'))//\a\b\c windows系统
console.log(path.join('/a','/b','/c', '../'))//\a\b\ windows系统
console.log(path.posix.join('/a','/b','/c')) // /a/b/c
resolve()解析路径,返回的是绝对路径
带斜杠的是绝对路径,不带的是相对路径
js
// 都是绝对路径返回最后一个
console.log(path.resolve('/a','/b','/c')) //C:\c
console.log(path.resolve('a', 'b', 'c')) //C:\CN\全栈AI\node\nodecode\a\b\c
console.log(path.posix.resolve('a', 'b', 'c')) // /CN/全栈AI/node/nodecode/a/b/c
console.log(path.win32.resolve('a', 'b', 'c')) // C:\CN\全栈AI\node\nodecode\a\b\c
console.log(path.resolve('./index.js')) //C:\CN\全栈AI\node\nodecode\index.js
parse 用于解析文件路径。它接受一个路径字符串作为输入,并返回一个包含路径各个组成部分的对象
js
console.log(path.parse('/home/user/dir/file.txt'))
// {
// root: '/',
// dir: '/home/user/dir',
// base: 'file.txt',
// ext: '.txt',
// name: 'file'
// }
format 与parse互补, 正好相反 在把对象转回字符串
js
console.log(path.format({
root: '/',
dir: '/home/user/dir',
base: 'file.txt',
ext: '.txt',
name: 'file'
}))
// /home/user/dir\file.txt
sep
js
// windows返回的是\ posix返回的是/
console.log(path.sep) // \
console.log(path.posix.sep) // /
| 环境 | 输入 | 输出 |
|---|---|---|
| Windows(正确转义) | 'C:\nvm4w\nodejs\...\myfile.html' |
myfile.html |
| Windows(未转义) | 'C:\nvm4w\nodejs...' |
意外行为或错误 |
Windows(使用 /) |
'C:/nvm4w/nodejs/.../myfile.html' |
myfile.html |
Linux/macOS(使用 /) |
'/usr/local/bin/myfile.html' |
myfile.html |
💡 最佳实践:
- 在 Windows 路径中使用 双反斜杠
\或 正斜杠/,Linux/macOS 使用 / - 使用
String.raw避免转义 - 使用
path.join()或path.resolve()构建路径 path.basename()会正确处理两种路径分隔符
OS
platform()获取操作系统平台
js
console.log(os.platform()) //win32 windows darwin(macOS) mac linux
release()返回操作系统的版本
js
console.log(os.release()) //10.xxxx win10
type()
js
//在 Linux 上返回 'Linux',在 macOS 上返回 'Darwin',在 Windows 上返回 'Windows_NT'
console.log(os.type())
version()
js
// 4、专业版和家庭版 旗舰版
console.log(os.version())
js
// 知道这些有什么用?
// webpack vite rollup open:true打开浏览器
// 判断不同的操作系统 分别调用对应的shell命令
const platform = os.platform();
function openBrowser(url) {
if (os.platform() === "darwin") {
// macOS
exec(`open ${url}`); //执行shell脚本
} else if (os.platform() === "win32") {
// Windows
exec(`start ${url}`); //执行windows的开发 shell脚本
} else {
// Linux, Unix-like
exec(`xdg-open ${url}`); //执行shell脚本
}
}
// Example usage
openBrowser("https://www.juejin.cn");
homedir()
js
// homedir 读取用户的目录,底层原理 %userprofile%(win) $HOME (MAC)
console.log(os.homedir()); //C:\Users\用户名
arch() cpu架构
cpus() 操作系统线程cpu的信息 ------ 电脑配置
js
console.log(os.cpus());
// Cpu利用率计算常用信息
// {
// model: '12th Gen Intel(R) Core(TM) i5-1240P',
// speed: 2112, //cpu运行时钟的速度
// times: {
// user: 2741750,
// nice: 0,
// sys: 6498078,
// idle: 359657468,
// irq: 918218
// }
// },
networkInterfaces()
js
console.log(os.networkInterfaces());
// {
// address: '127.0.0.1', ip地址
// netmask: '255.0.0.0', 子网掩码
// family: 'IPv4', ip版本 IPV4/IPV6
// mac: '00:00:00:00:00:00', 网卡的mac地址
// internal: true, 表示是不是内网ip false可以访问外网 true是内网
// cidr: '127.0.0.1/8' ip地址段
//}
process
arch
js
// 返回操作系统 CPU 架构 跟我们之前讲的os.arch 一样 'arm'、'arm64'、'ia32'、'mips'、'mipsel'、'ppc'、'ppc64'、's390'、's390x'、以及 'x64'
console.log(process.arch)
process.argv
js
// process.argv 接收参数 返回的是数组
// node index.js --version ------ ['使用什么东西运行的脚本(node)','运行的文件','--version']
console.log(process.argv)
process.cwd() 获取工作目录
和__dirname是一样的(但是在esm模式下,是用不了__dirname的) console.log(process.cwd(), __dirname)
memoryUsage()
js
// 用于获取当前进程的内存使用情况。看内存是否有超标或者泄露看性能
console.log(process.memoryUsage())
// {
// rss: 30932992, // 常驻集大小 这是进程当前占用的物理内存量,不包括共享内存和页面缓存。它反映了进程实际占用的物理内存大小
// heapTotal: 6438912, //堆区总大小 这是 V8 引擎为 JavaScript 对象分配的内存量。它包括了已用和未用的堆内存
// heapUsed: 5678624, //已用堆大小
// external: 423221, //外部内存使用量 这部分内存不是由 Node.js 进程直接分配的,而是由其他 C/C++ 对象或系统分配的
// arrayBuffers: 17606 //是用于处理二进制数据的对象类型,它使用了 JavaScript 中的 ArrayBuffer 接口。这个属性显示了当前进程中 ArrayBuffers 的数量 //
//}
exit()
js
// process.exit() 将强制进程尽快退出,即使仍有未完全完成的异步操作挂起
// 下面例子5不会被打印出来 因为在2秒钟的时候就被退出了。
setTimeout(() => {
console.log('5')
},5000)
setTimeout(() => {
process.exit()
},2000)
process.on('exit', ()=> {
console.log('进程被退出')
})
kill()
js
// process.kill(process.pid) 与exit类似,kill用来杀死一个进程,接受一个参数进程id 可以通过process.pid 获取
console.log(process.pid) //process.pid 获取
setTimeout(() => {
process.kill(process.pid)
}, 2000);
setTimeout(() => {
console.log('5')
},5000)
// 不会打印出5
env
js
// process.env 用于读取操作系统所有的环境变量,也可以修改和查询环境变量。
console.log(process.env)
// 修改 注意修改并不会真正影响操作系统的变量,而是只在当前线程生效,线程结束便释放。
// 用途 区分开发环境和生产环境------http https
process.env.ALLUSERSPROFILE = 'wwww'
console.log(process.env.ALLUSERSPROFILE)
// npm i cross-env 跨平台,windows会调用windows的shell命令(SET 环境变量) posix(export 环境变量)
// 这个库是干什么的 cross-env 是 跨平台设置和使用环境变量 不论是在Windows系统还是POSIX系统。同时,它提供了一个设置环境变量的脚本,使得您可以在脚本中以unix方式设置环境变量,然后在Windows上也能兼容运行
// 他的原理就是如果是windows 就调用SET 如果是posix 就调用export 设置环境变量
// set NODE_ENV=production #windows
// export NODE_ENV=production #posix
// package.json中设置
// "scripts": {
// "dev": "cross-env NODE_ENV=dev node index.js",
// "build": "cross-env NODE_ENV=prod node index.js"
// },
console.log(process.env.NODE_ENV === 'dev' ? '开发环境': '生产环境')
// npm run dev 开发环境
// npm run build 生产环境
child_process
Nodejs创建子进程共有7个API Sync同步API 不加是异步API
- spawn 执行命令
- exec 执行命令
- execFile 执行可执行文件
- fork 创建node子进程
- execSync 执行命令 同步执行
- execFileSync 执行可执行文件 同步执行
- spawnSync 执行命令 同步执行
exec execSync 执行命令
有字节上限,超出200kb会报错
spawn spawnSync
没有字节上限,因为返回的是个流,并且是实时返回
spawn 用于执行一些实时获取的信息因为spawn返回的是流边执行边返回,exec是返回一个完整的buffer,buffer的大小是200k,如果超出会报错,而spawn是无上限的。
spawn在执行完成后会抛出close事件监听,并返回状态码,通过状态码可以知道子进程是否顺利执行。exec只能通过返回的buffer去识别完成状态,识别起来较为麻烦
spawnSync用的比较少
execFile
child_process.execFile() 与 child_process.exec() 功能相似,唯一的不同在于不会生成新的 shell。更准确地来说,系统会使用新进程解析可执行文件,相比 child_process.exec() 更加高效。
child_process.execFile() 的可选参数与 child_process.exec() 相同。此外,由于没有生成新的 shell,所以不支持类似 I/O 重定向和文件名匹配的功能:
exec -> execFile -> spawn exec是底层通过execFile实现 execFile底层通过spawn实现
js
// execFile 执行可执行文件
// execFile(path.resolve(__dirname, './bat.cmd'),null,(err,stdout) => {
// if (err) {
// console.error('执行批处理文件时出现错误:', err);
// return;
// }
// console.log(stdout.toString())
// })
execFile('cmd.exe', ['/c', path.resolve('bat.cmd')], (err, stdout, stderr) => {
if (err) {
console.error(err)
return
}
console.log(stdout)
})
// console.log(path.resolve('./bat.cmd'))
| 参数 | 全称 | 含义 |
|---|---|---|
/c |
Carry out / Close | 执行后面指定的命令,执行完毕后立即退出 cmd 进程 |
/k |
Keep | 执行后面指定的命令,执行完毕后保持窗口打开(不退出) |
fork 只能接受js模块 返回一个子进程
js
//index.js
// fork 只能接受js模块 返回一个子进程
// 主进程可以给子进程发送消息
// nodejs弱点不适合做CPU密集型应用,因为是单线程会阻塞,可以把耗时代码放在子进程中执行,执行完之后可以把子进程的值吐出来
// 底层是通过IPC通讯的,基于libuv实现的,跟别调用不同操作系统的底层API,(WINDOWS named pipe) (posix unix domain socket)
const testProcess = fork('./test.js')
// 主进程给子进程发消息
testProcess.send('我是主进程')
// 主进程收到子进程的消息
testProcess.on('message', (res) => {
console.log(res)
})
js
//test.js
// 子进程收到主进程的消息
process.on('message',(msg) => {
console.log('收到消息了',msg)
})
// 子进程给主进程发消息
process.send('我是子进程,收到请回复')
FS
const fs = require('node:fs')
const fs2 = require('node:fs/promises')
1、读取文件 readFile readFileSync
2、可读流
3、创建文件夹
4、删除rm
5、重命名
6、监听文件变化
7、源码
8、注意事项 事件循环
读取文件 readFile readFileSync
js
// 1、读取文件
// 不加sync就是异步的
fs.readFile('./fs.txt',{
encoding: 'utf-8',
flag: 'r',
},(err,data) => {
if(err) throw err
console.log(data)
})
// 同步的方式会阻塞下面的代码
const file = fs.readFileSync('./fs.txt')
console.log(file.toString("utf-8"))
fs2.readFile('./fs.txt').then(res => {
console.log(res.toString())
}).catch(err => {})
// 源码
// fs的源码是通过 C++ 层的 FSReqCallback 这个类 对libuv 的uv_fs_t 的一个封装,其实也就是将我们fs 的参数透传给 libuv 层
// 注意事项 事件循环
// const fs = require('node:fs')
fs.readFile('./fs.txt', {
encoding: 'utf-8',
flag: 'r'
}, (err, dataStr) => {
if (err) throw err
console.log('fs')
})
setImmediate(() => {
console.log('setImmediate')
})
// setImmediate
// fs
// 为什么先走setImmediate 呢,而不是fs
// Node.js 读取文件的时候是使用libuv进行调度的
// 而setImmediate是由V8进行调度的
// 文件读取完成后 libuv 才会将 fs的结果 推入V8的队列
可读流readStream
js
// 2、可读流
// 处理大文件的时候,把文件一段一段返回来,不是一下读完。
const readStream = fs.createReadStream('./fs.txt')
readStream.on('data', (chunk) => {
console.log(chunk.toString())
})
readStream.on('end', () => {
console.log('读完了~~~~~~')
})
创建文件夹mkdirSync
js
// 3、创建文件夹 {recursive: true}递归可以新增多个文件夹
fs.mkdirSync('./xiaoman')
fs.mkdirSync('./xiaoman/zhongman/daman',{
recursive: true
})
删除文件rmSync
js
// 4、删除文件 {recursive: true}递归可以删除多个文件夹
fs.rmSync('./xiaoman',{recursive: true})
重命名renameSync
js
// 5、重命名
fs.renameSync('./fs.txt', './fs2.txt')
监听文件变化watch
js
// 6、监听文件变化 event(rename------重命名,change------修改文件内容)
fs.watch('./fs2.txt', (event,filename) => {
console.log(event,filename)
})
写入文件writeFileSync
js
// 9、写入文件
fs.writeFileSync('./fs.txt', 'java之父')
// 10、追加写入文件 两种方式
fs.writeFileSync('./fs.txt','\nVue之父。。。',{
flag: 'a' //append
})
fs.appendFileSync('./fs.txt', '\nunshift')
创建可写流createWriteStream
js
// 11、创建可写流
// 大量的数据分批插入
let verse= [
'待到秋来九月八',
'我花开后百花杀',
'冲天香阵透长安',
'满城尽带黄金甲'
]
let writeStream = fs.createWriteStream('fs.txt')
verse.forEach(item => {
writeStream.write(item+ '\n')
})
writeStream.end()
writeStream.on('finish',()=>{
console.log('写入完成')
})
软连接 硬链接
硬链接的作用和用途如下:
- 文件共享:硬链接允许多个文件名指向同一个文件,这样可以在不同的位置使用不同的文件名引用相同的内容。这样的共享文件可以节省存储空间,并且在多个位置对文件的修改会反映在所有引用文件上。
- 文件备份:通过创建硬链接,可以在不复制文件的情况下创建文件的备份。如果原始文件发生更改,备份文件也会自动更新。这样可以节省磁盘空间,并确保备份文件与原始文件保持同步。
- 文件重命名:通过创建硬链接,可以为文件创建一个新的文件名,而无需复制或移动文件。这对于需要更改文件名但保持相同内容和属性的场景非常有用。
软链接的一些特点和用途如下:
- 软链接可以创建指向文件或目录的引用。这使得你可以在不复制或移动文件的情况下引用它们,并在不同位置使用不同的文件名访问相同的内容。
- 软链接可以用于创建快捷方式或别名,使得你可以通过一个简短或易记的路径来访问复杂或深层次的目录结构。
- 软链接可以用于解决文件或目录的位置变化问题。如果目标文件或目录被移动或重命名,只需更新软链接的目标路径即可,而不需要修改引用该文件或目录的其他代码。
js
// 我们可以创建一个可写流 打开一个通道,可以一直写入数据,用于处理大量的数据写入,写入完成之后调用end 关闭可写流,监听finish 事件 写入完成
// 12、软连接 硬链接 pnpm 底层原理
// 硬链接------共享同一个内存地址:共享文件、做备份文件,如果原始文件删掉,备份文件不受影响
fs.linkSync('./fs.txt', './fs2.txt')
// 软连接, 很像windows的快捷方式 需要以以管理员方式运行,如果原始文件删掉,备份文件会受影响
fs.symlinkSync('./fs.txt', './fs3.txt')
crypto
crypto模块的目的是为了提供通用的加密和哈希算法。用纯JavaScript代码实现这些功能不是不可能,但速度会非常慢。nodejs用C/C++实现这些算法后,通过crypto这个模块暴露为JavaScript接口,这样用起来方便,运行速度也快。
对称加密算法
对称加密是一种简单而快速的加密方式,它使用相同的密钥(称为对称密钥)来进行加密和解密。这意味着发送者和接收者在加密和解密过程中都使用相同的密钥。对称加密算法的加密速度很快,适合对大量数据进行加密和解密操作。然而,对称密钥的安全性是一个挑战,因为需要确保发送者和接收者都安全地共享密钥,否则有风险被未授权的人获取密钥并解密数据。
非对称加密
非对称加密使用一对密钥,分别是公钥和私钥。发送者使用接收者的公钥进行加密,而接收者使用自己的私钥进行解密。公钥可以自由分享给任何人,而私钥必须保密。非对称加密算法提供了更高的安全性,因为即使公钥泄露,只有持有私钥的接收者才能解密数据。然而,非对称加密算法的加密速度相对较慢,不适合加密大量数据。因此,在实际应用中,通常使用非对称加密来交换对称密钥,然后使用对称加密算法来加密实际的数据。
哈希函数
哈希函数具有以下特点:
- 固定长度输出:不论输入数据的大小,哈希函数的输出长度是固定的。例如,常见的哈希函数如 MD5 和 SHA-256 生成的哈希值长度分别为 128 位和 256 位。
- 不可逆性:哈希函数是单向的,意味着从哈希值推导出原始输入数据是非常困难的,几乎不可能。即使输入数据发生微小的变化,其哈希值也会完全不同。
- 唯一性:哈希函数应该具有较低的碰撞概率,即不同的输入数据生成相同的哈希值的可能性应该非常小。这有助于确保哈希值能够唯一地标识输入数据。
使用场景
- 我们可以避免密码明文传输 使用md5加密或者sha256
- 验证文件完整性,读取文件内容生成md5 如果前端上传的md5和后端的读取文件内部的md5匹配说明文件是完整的
js
const crypto = require("node:crypto");
// 对称加密算法
// 双方协商定义一个密钥以及iv
// 第一个参数 algorithm 接受一个算法 aes-256-cbc
// 第二个参数 key 也就是密钥 32位
// 第三个参数 iv 初始化向量 支持16位 保证生成的密钥串每次是不一样的,缺少位数还可以进行补码
let key = crypto.randomBytes(32);
let iv = Buffer.from(crypto.randomBytes(16));
const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
// // 对输入数据进行加密,并输出加密结果的十六进制表示
cipher.update("小满zs", "utf-8", "hex");
const result = cipher.final("hex"); //输出密文 16进制
console.log(result);
// 解密 相同的算法 相同的key 相同的iv
const de = crypto.createDecipheriv("aes-256-cbc", key, iv);
de.update(result, "hex", "utf-8");
const decrypted = de.final("utf-8");
console.log("Decrypted:", decrypted);
//非对称加密
// 生成公钥和私钥
// 私钥只能是管理员拥有的 不能对外公开
// 公钥可以对外公开
// 生成 RSA 密钥对
const { privateKey, publicKey } = crypto.generateKeyPairSync("rsa", {
modulusLength: 2048, //长度越长越安全 越慢
});
// 要加密的数据
const text = "小满zs";
// 使用公钥进行加密
const encrypted = crypto.publicEncrypt(publicKey, Buffer.from(text, "utf-8"));
// 使用私钥进行解密
const decrypted = crypto.privateDecrypt(privateKey, encrypted);
console.log(decrypted.toString());
// 哈希函数
// 不能被解密 因为他时单向的 不可逆的
// 不是很安全 具有唯一性
// md5 包裹一下存储到数据库 不可能铭文存储的
// 撞库,可以去碰你的密码
let hash = crypto.createHash("md5"); //还有md5 sha256
hash.update("小满zs");
console.log(hash.digest("hex"));
// 读取文件内容 转换成md5 上传给服务端 后端拿到文件内容生成md5
// 跟前端md5匹配 如果一致 文件就没问题 如果不一致 文件就有问题
// 校验文件的一致性
zlib
对数据或者文件压缩和解压缩的功能。服务器资源有限,一般前端上传的文件,都会变成一个压缩包,存储在服务器上/oss上,可以大大节省空间。可以对流 http内容 , 以便在应用程序中减少数据的传输大小和提高性能。
zlib 模块的主要作用如下:
- 数据压缩:使用
zlib模块可以将数据以无损压缩算法(如 Deflate、Gzip)进行压缩,减少数据的大小。这在网络传输和磁盘存储中特别有用,可以节省带宽和存储空间。 - 数据解压缩:
zlib模块还提供了对压缩数据的解压缩功能,可以还原压缩前的原始数据。 - 流压缩:
zlib模块支持使用流(Stream)的方式进行数据的压缩和解压缩。这种方式使得可以对大型文件或网络数据流进行逐步处理,而不需要将整个数据加载到内存中。 - 压缩格式支持:
zlib模块支持多种常见的压缩格式,如 Gzip 和 Deflate。这些格式在各种应用场景中广泛使用,例如 HTTP 响应的内容编码、文件压缩和解压缩等。
使用 zlib 模块进行数据压缩和解压缩可以帮助优化应用程序的性能和资源利用。通过减小数据的大小,可以减少网络传输的时间和带宽消耗,同时减少磁盘上的存储空间。此外,zlib 模块还提供了丰富的选项和方法,使得开发者可以根据具体需求进行灵活的压缩和解压缩操作。
两个压缩算法:gzip、deflate。
gzip 和 deflate 区别
- 压缩算法:Gzip 使用的是 Deflate 压缩算法,该算法结合了 LZ77 算法和哈夫曼编码。LZ77 算法用于数据的重复字符串的替换和引用 ,而哈夫曼编码 用于进一步压缩数据。
- 压缩效率:Gzip 压缩通常具有更高的压缩率,因为它使用了哈夫曼编码来进一步压缩数据。哈夫曼编码根据字符的出现频率,将较常见的字符用较短的编码表示,从而减小数据的大小。
- 压缩速度:相比于仅使用 Deflate 的方式,Gzip 压缩需要更多的计算和处理时间,因为它还要进行哈夫曼编码的步骤。因此,在压缩速度方面,Deflate 可能比 Gzip 更快。
- 应用场景:Gzip 压缩常用于文件压缩、网络传输和 HTTP 响应的内容编码。它广泛应用于 Web 服务器和浏览器之间的数据传输,以减小文件大小和提高网络传输效率。
scss
Deflate比Gzip(哈夫曼编码)快
Deflate适用于网络传输
Gzip适用于文件压缩
压缩:createGzip() createDeflate() gzipSync() deflateSync()
解压缩:createGunzip() createInflate()
文件压缩
php
const zlib = require('node:zlib')
const fs = require('node:fs')
// 压缩createGzip() .gz createDeflate() .deflate
// gzip
// 创建可读流和可写流
const readStream = fs.createReadStream('fs.txt')// 创建可读流,读取名为 fs.txt 的文件
const writeStream = fs.createWriteStream('fs.txt.gz')// 创建可写流,将压缩后的数据写入 index.txt.gz 文件
// 将fs.txt压缩一下 放入到写入流writeStream
// 使用管道将可读流中的数据通过 Gzip 压缩,再通过管道传输到可写流中进行写入
readStream.pipe(zlib.createGzip()).pipe(writeStream)
// 解压缩 createGunzip()
const readStream2 = fs.createReadStream('fs.txt.gz')
const writeStream2 = fs.createWriteStream('index2.txt')
readStream2.pipe(zlib.createGunzip()).pipe(writeStream2)
// deflate 无损压缩 deflate 使用 createDeflate方法
const readStream = fs.createReadStream('fs.txt')
const writeStream = fs.createWriteStream('fs.txt.deflate') //.deflate
readStream.pipe(zlib.createDeflate()).pipe(writeStream)
// 解压createInflate()
const readStream = fs.createReadStream('fs.txt.deflate')
const writeStream = fs.createWriteStream('fs4.deflate') //.deflate
readStream.pipe(zlib.createInflate()).pipe(writeStream)
网络压缩
javascript
// 网络压缩
const zlib = require('node:zlib')
const http = require('node:http')
// 8.2kb gzip 0.2kb
const server = http.createServer((req, res) => {
const txt = '小满zs'.repeat(1000)
res.setHeader('Content-type','text/plain;charset=utf-8')
res.end(txt)
})
server.listen(3000, () => {
console.log('服务启动成功')
})
// gzip 0.2kb
const server = http.createServer((req, res) => {
const txt = '小满zs'.repeat(1000)
res.setHeader('Content-Encoding', 'gzip')
res.setHeader('Content-type','text/plain;charset=utf-8')
let result = zlib.gzipSync(txt)
res.end(result)
})
server.listen(3000, () => {
console.log('服务启动成功')
})
// deflate 0.2kb
const server = http.createServer((req, res) => {
const txt = '小满zs'.repeat(1000)
res.setHeader('Content-Encoding', 'deflate')
res.setHeader('Content-type','text/plain;charset=utf-8')
let result = zlib.deflateSync(txt)
res.end(result)
})
server.listen(3000, () => {
console.log('服务启动成功')
})
createGzip() 和 gzipSync()
createGzip() 和 gzipSync() 都是 Node.js 中 zlib 模块用于 Gzip 压缩的方法
简单来说:
createGzip():是流式(Stream) 的,适合处理大文件或网络数据流。gzipSync():是同步一次性 的,适合处理体积小的数据,如字符串或小Buffer
| 特性 | createGzip() |
gzipSync() |
|---|---|---|
| 工作方式 | 返回一个 Transform 流。数据是一块一块地流进去,压缩后再一块一块地流出来。 | 是一个同步函数,一次性接收完整的输入数据(Buffer 或字符串),压缩后一次性返回完整的压缩结果。 |
| 对事件循环的影响 | 非阻塞。压缩在后台进行,不会阻塞 Node.js 事件循环,适合处理大量数据。 | 阻塞。压缩完成前,Node.js 无法处理其他任何任务。如果数据量大,会导致程序"卡死"。 |
| 内存使用 | 高效。以流的形式处理数据,不需要将整个数据加载到内存中,内存占用稳定。 | 高。需要将整个输入数据一次性放入内存进行压缩。如果文件很大,可能撑爆内存。 |
| 典型应用场景 | 压缩大文件(如日志、数据库备份)、HTTP 响应的实时压缩(如 Express 或 WebSocket 数据流)。 | 压缩很小的配置对象、字符串、或是一次性完成的简单压缩任务。 |
💡 什么时候该用哪个?
选择哪个方法,主要看你的数据规模 和对性能的要求。
-
使用
createGzip()的时机:- 处理大文件:比如压缩几 GB 的日志文件时,用流式处理是标准做法。
- 网络传输 :在 Web 服务器中,实时压缩并发送响应给客户端。配合
pipeline可以很好地处理错误和背压(backpressure)。 - 数据源源不断:例如从数据库中流式读取数据,边读边压缩发送,不需要等所有数据就绪。
-
使用
gzipSync()的时机:- 处理小数据:压缩一个小的 JSON 对象、一个短字符串。数据量很小,同步操作耗时极短,对事件循环影响可以忽略不计。
- 代码简单直接 :在一些简单的脚本或一次性任务中,使用
gzipSync()可以让代码更简洁明了。
⚠️ 注意事项
gzipSync()不能用于大文件 :如果数据超过几百 KB,就要谨慎考虑使用gzipSync(),优先选择createGzip(),避免阻塞主线程和内存溢出。createGzip()与gzip()的区别 :zlib模块还有一个异步的gzip()方法,它和gzipSync()一样是一次性处理数据,但它是非阻塞的。它介于流式和同步阻塞之间,也是一种选择。
ffmpeg
FFmpeg 是一个开源的跨平台多媒体处理工具,可以用于处理音频、视频和多媒体流。它提供了一组强大的命令行工具和库,可以进行视频转码、视频剪辑、音频提取、音视频合并、流媒体传输等操作。
FFmpeg 的主要功能和特性:
- 格式转换:FFmpeg 可以将一个媒体文件从一种格式转换为另一种格式,支持几乎所有常见的音频和视频格式,包括 MP4、AVI、MKV、MOV、FLV、MP3、AAC 等。
- 视频处理:FFmpeg 可以进行视频编码、解码、裁剪、旋转、缩放、调整帧率、添加水印等操作。你可以使用它来调整视频的分辨率、剪辑和拼接视频片段,以及对视频进行各种效果处理。
- 音频处理:FFmpeg 可以进行音频编码、解码、剪辑、混音、音量调节等操作。你可以用它来提取音频轨道、剪辑和拼接音频片段,以及对音频进行降噪、均衡器等处理。
- 流媒体传输:FFmpeg 支持将音视频流实时传输到网络上,可以用于实时流媒体服务、直播和视频会议等应用场景。
- 视频处理效率高:FFmpeg 是一个高效的工具,针对处理大型视频文件和高分辨率视频进行了优化,可以在保持良好质量的同时提供较快的处理速度。
- 跨平台支持:FFmpeg 可以在多个操作系统上运行,包括 Windows、MacOS、Linux 等,同时支持多种硬件加速技术,如 NVIDIA CUDA 和 Intel Quick Sync Video。
php
const {execSync} = require('child_process')
// 基本格式转换 avi mp4 gif等
execSync('ffmpeg -i test.mp4 test.gif',{stdio: 'inherit'})
// 提取视频中的音频
execSync('ffmpeg -i test.mp4 test.mp3',{stdio: 'inherit'})
// 裁剪视频
execSync('ffmpeg -ss 10 -to 20 -i test.mp4 test2.mp4',{stdio: 'inherit'})
// 加水印
execSync('ffmpeg -i test.mp4 -vf drawtext=text="XMZS":fontsize=30:x=10:y=10:fontcolor=green test3.mp4',{stdio: 'inherit'})
// 删除水印
execSync('ffmpeg -i test3.mp4 -vf delogo=w=120:h=30:x=10:y=10 test4.mp4',{stdio: 'inherit'})
Markdown 转 html
css
npm i ejs marked browser-sync
什么是 Markdown ?
Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档。
我们需要用到三个库实现
- EJS :一款强大的JavaScript模板引擎,在HTML中嵌入动态内容。使用EJS,您可以轻松地将Markdown转换为美观的HTML页面。
- Marked :一个流行的Markdown解析器和编译器,它可以将Markdown语法转换为HTML标记。Marked是一个功能强大且易于使用的库,它为您提供了丰富的选项和扩展功能,以满足各种转换需求。
- BrowserSync :一个强大的开发工具,它可以帮助您实时预览和同步您的网页更改 。当您对Markdown文件进行编辑并将其转换为HTML时,BrowserSync可以自动刷新您的浏览器,使您能够即时查看转换后的结果。
javascript
//Marked
const marked = require('marked')
marked.parse(readme.toString()) //调用parse即可
js
//browserSync
//创建browser 并且开启一个服务 设置根目录和 index.html 文件
const browserSync = require('browser-sync')
const openBrowser = () => {
const browser = browserSync.create()
browser.init({
server: {
baseDir: './',
index: 'index.html',
}
})
return browser
}
js
const ejs = require('ejs')
const fs = require('node:fs')
const marked = require('marked')
const browserSync = require('browser-sync'); // 导入browser-sync库,用于实时预览和同步浏览器
let browser;
const openBrowser = () => {
browser = browserSync.create()
browser.init({
server: {
baseDir: './',
index: 'index.html',
}
})
}
// 没有热更新写法
const init = () => {
const file = fs.readFileSync('./README.md', 'utf-8') //读取到md文件
// ejs.renderFile 是 EJS 模板引擎提供的方法,用于读取一个 .ejs 模板文件,并将数据填充进去,最终生成一个 HTML 字符串(下面回调函数的data)
ejs.renderFile('template.ejs', {
content: marked.parse(file), //marked.parse(file) Markdown语法转换为HTML标记
title: 'markdown to html'
},(err,data) => {
// data 就是渲染后的 HTML 字符串
if(err) throw(err)
let writeStream = fs.createWriteStream('index.html')
writeStream.write(data)
writeStream.end()
writeStream.on('finish',()=>{
// console.log('写入完成')
openBrowser()
})
})
}
init()
// 有热更新写法
const init = (callback) => {
const file = fs.readFileSync('./README.md', 'utf-8')
ejs.renderFile('template.ejs', {
content: marked.parse(file),
title: 'markdown to html'
},(err,data) => {
if(err) throw(err)
fs.writeFileSync('index.html',data)
// openBrowser()
callback && callback()
})
}
// 热更新
fs.watchFile('README.md',(curr, prev) => {
// fs.writeFileSync()
// console.log(event,filename)
if(curr.mtime != prev.mtime) {
init(() => {
// 重新刷新页面
browser.reload()
})
}
})
init(() => {
openBrowser()
})
js
//template.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title>
</head>
<body>
<%- content %>
</body>
</html>
HTTP web服务器
一般帮我们创建web服务器,跟前端进行交互的。前端一般会发送ajax请求,需要一个后端的地址,通过不同的路由,进行不同的业务逻辑处理。也可以做代理服务器,vuecli webpack vite的proxy代理也是基于http模块实现的,可以做文件存储服务器。
arduino
http要处理的:
1、请求方式 post get put delete patch options head..
2、路由 http://localhost:98/login /api/list
3、get 传参 跟post不一样
js
const http = require('node:http')
const url = require('node:url')
// req接受前端的信息 res给前端返回的信息
http.createServer((req,res) => {
// 解析请求的 URL,获取路径和查询参数
const {pathname, query } = url.parse(req.url,true)
if(req.method === 'POST') { // 检查请求方法是否为 POST
// 检查路径是否为 '/login'
if(pathname === '/login') {
let data = ''
req.on('data', (chunk) => {
// console.log('data',chunk.toString(), 'data')
data+=chunk // 获取 POST 请求的数据
})
req.on('end', () => {
res.setHeader('Content-Type', 'application/json'); // 设置响应头的 Content-Type 为 'application/json'
res.statusCode = 200; // 设置响应状态码为 200
res.end(data); // 将获取到的数据作为响应体返回
})
}else {
res.setHeader('Content-Type', 'application/json'); // 设置响应头的 Content-Type 为 'application/json'
res.statusCode = 404; // 设置响应状态码为 404
res.end('Not Found'); // 返回 'Not Found' 作为响应体
}
}else if(req.method === 'GET') {
if (pathname === '/get') { // 检查路径是否为 '/get'
console.log(query.a, 'a'); // 打印查询参数中的键名为 'a' 的值
res.end('get success'); // 返回 'get success' 作为响应体
}
}
}).listen(98,() => {
console.log('98端口号启动成功')
})
bash
POST http://localhost:98/login HTTP/1.1
Content-Type: application/json
{
name: '小满zs'
}
GET http://localhost:98/get?a=1&b=2 HTTP/1.1