MST

星途 面试题库

面试题:Node.js Stream 实现文件解压与压缩的错误处理与性能优化综合考量

在实现 Node.js 使用 Stream 进行文件压缩与解压的项目中,不仅要保证功能正确,还要注重错误处理和性能优化。请详细说明从项目架构设计层面,如何构建一个健壮且高效的文件压缩解压模块。例如,如何优雅地处理压缩解压过程中的各种错误(如文件损坏、权限不足等),以及如何在多线程或多核环境下利用 Stream 提升整体性能。给出详细的设计思路和关键代码片段。
46.5万 热度难度
前端开发Node.js

知识考点

AI 面试

面试题答案

一键面试

项目架构设计思路

  1. 模块拆分:将文件压缩解压功能拆分为不同的模块,如压缩模块、解压模块、错误处理模块、配置模块等,提高代码的可维护性和复用性。
  2. 错误处理
    • 文件相关错误:在打开文件、读取文件、写入文件等操作时,捕获错误并进行相应处理。例如权限不足错误(EACCES),文件不存在错误(ENOENT)等。
    • 压缩解压错误:在压缩和解压流的error事件中捕获错误,如解压时文件损坏可能导致的校验和错误等。
  3. 性能优化
    • Stream 合理使用:利用 Node.js 的Stream特性,实现数据的流式处理,避免一次性加载整个文件到内存,从而提升性能。
    • 多线程与多核利用:虽然 Node.js 是单线程的,但可以通过child_process模块创建子进程,充分利用多核 CPU 的优势。每个子进程可以独立处理一部分文件的压缩或解压任务。

关键代码片段

压缩模块

const { createGzip } = require('zlib');
const { createReadStream, createWriteStream } = require('fs');

function compressFile(sourceFilePath, targetFilePath) {
    const readStream = createReadStream(sourceFilePath);
    const writeStream = createWriteStream(targetFilePath);
    const gzip = createGzip();

    readStream.on('error', (err) => {
        console.error(`读取文件错误: ${err.message}`);
        // 处理读取文件错误,如关闭写入流等操作
        writeStream.end();
    });

    writeStream.on('error', (err) => {
        console.error(`写入文件错误: ${err.message}`);
        // 处理写入文件错误,如关闭读取流等操作
        readStream.destroy();
    });

    gzip.on('error', (err) => {
        console.error(`压缩错误: ${err.message}`);
        // 处理压缩错误,如关闭读取流和写入流
        readStream.destroy();
        writeStream.end();
    });

    readStream.pipe(gzip).pipe(writeStream);
}

解压模块

const { createGunzip } = require('zlib');
const { createReadStream, createWriteStream } = require('fs');

function decompressFile(sourceFilePath, targetFilePath) {
    const readStream = createReadStream(sourceFilePath);
    const writeStream = createWriteStream(targetFilePath);
    const gunzip = createGunzip();

    readStream.on('error', (err) => {
        console.error(`读取文件错误: ${err.message}`);
        // 处理读取文件错误,如关闭写入流等操作
        writeStream.end();
    });

    writeStream.on('error', (err) => {
        console.error(`写入文件错误: ${err.message}`);
        // 处理写入文件错误,如关闭读取流等操作
        readStream.destroy();
    });

    gunzip.on('error', (err) => {
        console.error(`解压错误: ${err.message}`);
        // 处理解压错误,如关闭读取流和写入流
        readStream.destroy();
        writeStream.end();
    });

    readStream.pipe(gunzip).pipe(writeStream);
}

多进程处理示例

const { fork } = require('child_process');

function compressFilesInParallel(sourceFilePaths, targetFilePaths) {
    const numFiles = sourceFilePaths.length;
    const childProcesses = [];

    for (let i = 0; i < numFiles; i++) {
        const child = fork('./compressWorker.js');
        child.send({ sourceFilePath: sourceFilePaths[i], targetFilePath: targetFilePaths[i] });
        childProcesses.push(child);

        child.on('error', (err) => {
            console.error(`子进程错误: ${err.message}`);
        });

        child.on('exit', (code) => {
            if (code === 0) {
                console.log(`子进程 ${i} 压缩完成`);
            } else {
                console.error(`子进程 ${i} 压缩失败,退出码: ${code}`);
            }
        });
    }
}

// compressWorker.js 内容示例
process.on('message', (msg) => {
    const { sourceFilePath, targetFilePath } = msg;
    // 调用上述压缩函数进行文件压缩
    compressFile(sourceFilePath, targetFilePath);
    process.exit(0);
});

通过以上设计思路和代码片段,可以构建一个健壮且高效的文件压缩解压模块。