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
相关产品推荐
相关产品推荐

