You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NestJS(TypeScript)无需本地解压修改Zip流内文件名的方法

实现思路与可用库

核心思路

不用本地解压修改Zip文件名,本质是流式解析原Zip + 流式生成新Zip:

  • 一边读取原Zip的流,逐条解析内部条目
  • 修改条目文件名后,直接写入新的Zip输出流
  • 全程不用把整个Zip文件存到本地或全加载到内存,适合处理大文件

推荐库组合

使用yauzl(流式读取Zip) + zip-stream(流式生成Zip),两者都支持TypeScript,且有完善的类型定义:

npm install yauzl zip-stream @types/yauzl @types/zip-stream

Nest服务中的实现代码示例

import { Controller, Get, Res, HttpException, HttpStatus } from '@nestjs/common';
import { Response } from 'express';
import * as yauzl from 'yauzl';
import * as ZipStream from 'zip-stream';
import { fetch } from 'undici'; // 或用你项目中的HTTP客户端

@Controller('zip')
export class ZipController {
  @Get('rename')
  async renameZipEntries(@Res() res: Response) {
    try {
      // 从其他服务获取Zip流
      const remoteRes = await fetch('http://your-remote-service/download-zip');
      if (!remoteRes.ok) {
        throw new Error(`Remote service error: ${remoteRes.status}`);
      }
      const inputStream = remoteRes.body;

      // 设置响应头,返回修改后的Zip
      res.setHeader('Content-Type', 'application/zip');
      res.setHeader('Content-Disposition', 'attachment; filename="renamed-files.zip"');

      // 初始化Zip输出流,直接pipe到响应
      const zipOutputStream = new ZipStream();
      zipOutputStream.pipe(res);

      // 流式解析原Zip
      yauzl.fromStream(inputStream, { lazyEntries: true }, (err, zipFile) => {
        if (err) throw err;

        // 开始读取第一个条目
        zipFile.readEntry();

        zipFile.on('entry', (entry) => {
          // 自定义文件名修改逻辑,这里给所有文件加前缀"renamed_"
          const newFileName = entry.fileName.startsWith('renamed_') 
            ? entry.fileName 
            : `renamed_${entry.fileName}`;

          // 处理目录条目
          if (/\/$/.test(entry.fileName)) {
            zipOutputStream.entry(null, { name: newFileName }, () => {
              zipFile.readEntry(); // 处理下一个条目
            });
            return;
          }

          // 处理文件条目:读取原文件流,写入新Zip
          zipFile.openReadStream(entry, (streamErr, entryStream) => {
            if (streamErr) throw streamErr;

            zipOutputStream.entry(entryStream, { name: newFileName }, () => {
              entryStream.close();
              zipFile.readEntry(); // 处理下一个条目
            });
          });
        });

        // 原Zip解析完成后,结束输出流
        zipFile.on('end', () => {
          zipOutputStream.finalize();
        });

        // 监听流错误,避免服务崩溃
        zipFile.on('error', (streamErr) => {
          throw streamErr;
        });
      });
    } catch (error) {
      throw new HttpException(`处理Zip失败: ${error.message}`, HttpStatus.INTERNAL_SERVER_ERROR);
    }
  }
}

注意事项

  • lazyEntries: true必须设置:让yauzl逐条读取条目,而非一次性加载所有条目信息到内存,避免大Zip导致内存溢出。
  • 要区分目录和文件条目:目录条目不需要内容,直接创建空条目即可。
  • 错误处理:必须监听流的error事件,同时用try/catch包裹异步逻辑,避免未捕获异常导致服务崩溃。
  • 备选库:如果觉得yauzl的API偏底层,可以用node-stream-zip,它的API更简洁,同样支持流式处理,安装命令:npm install node-stream-zip @types/node-stream-zip

内容的提问来源于stack exchange,提问作者eyal gromzin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 22:15:49