Node.js的 Stream流 与 子进程

30 阅读1分钟

为什么要使用流?

当读取/写入一个很大文件的时候,会占用很大的内存。为了节约内存降低开销,我们引入流让数据像水流一样一点一点写入

如何使用流?

Stream流

const fs = require('fs')
const stream = fs.createWriteStream('./big_file.txt')
for(let i=0; i<=10000; i++){
    stream.write(`这是第 ${i} 行内容,我们有一堆内容!!!`)
}
stream.end()    // 别忘了关掉 stream
console.log('done')
uTools_1708417458799.png

pipe管道

stream1.pipe(stream2)
a.pipe(b).pipe(c)    // 链式操作,等价于下面
a.pipe(b)
b.pipe(c)
uTools_1708417440436.png

Stream分类

名称特点
Readable可读
Writable可写
Duplex可读可写(双向)
Transform可读可写(变化)
uTools_1708418417488.png uTools_1708418487601.png uTools_1708418510709.png

Nodejs中的Stream

uTools_1708418737570.png

子进程 child_process

断点

node --inspect-brk 4.js    // 设置断点

随便打开一个网页后等到出现这个标志,点开后设置断点并运行 uTools_1702176517046.png

uTools_1702176680509.png

uTools_1702183845583.png

const fs = require('fs')

function writeOneMillionTimes(writer, data) {
    let i = 10000;
    write();
    function write() {
        let ok = true;
        do {
            i--;
            if (i === 0) {
                writer.write(data);
            } else {
                ok = writer.write(data);
                if (ok === false) {
                    console.log('不能在写了')
                }
            }
        } while (i > 0 && ok);
        if (i > 0) {
            writer.once('drain', () => {
                console.log('干涸了')
                write()
            });
        }
    }
}

const writer = fs.createWriteStream('./big_file.txt')
writeOneMillionTimes(writer, 'hellow world')

并行与并发

exec(cmd, options, fn) 【一般不允许用,因为容易让用户输入不合适的命令导致出错(注入风险)】

const child_process = require('child_process')
const { exec } = child_process

exec('ls ../', (error, stdout, stderr) => {
    console.log(error)
    console.log(stdout)
    console.log(stderr)
})

const child_process = require('child_process')
const { exec } = child_process

const streams = exec('ls -l ../')

streams.stdout.on('data', (chunk) => {
    console.log(chunk)
})
streams.stdout.on('data')

Promise

const util = require("util")
const child_process = require('child_process')
const { exec } = child_process

const exec2 = util.promisify(exec)

exec2('ls ../').then((data) => {
    console.log(data.stdout)
})

漏洞

const util = require("util")
const child_process = require('child_process')
const { exec } = child_process

const exec2 = util.promisify(exec)
const useInput = '. && rm -f /'

exec2(`ls ${userInput}`).then((data) => {
    console.log(data.stdout)
})

execFile 【比较推荐使用】

const child_process = require('child_process')
const { execFile } = child_process

const useInput = "."

execFile("ls", ["-la", useInput], (error, stdout) => {
    console.log(stdout)
})

options

uTools_1702218120776.png

spawn 【推荐使用,能用这个就不用execFile】

可以创建一个进程并获取结果,只支持流的形式获取结果

const child_process = require('child_process')
const { spawn } = child_process

const useInput = "."

const streams = spawn("ls", ["-la", useInput], {
    cwd: 'C:\\',
    env: { NODE_ENV: 'development' }
})

streams.stdout.on('data', (chunk) => {
    console.log(chunk.toString())
})

fork【强烈推荐使用 ! 】