Node16环境下NestJS使用JSZip打包CSV为zip报blob不支持错误
报错根因
你遇到这个问题是三个错误导致的:
blob是浏览器环境专属的Web API,Node.js 16原生没有提供浏览器端的Blob实现,JSZip检测到当前运行环境不支持Blob就会抛出这个错误- FileSaver是纯前端浏览器库,唯一作用是触发浏览器的文件下载弹窗,根本无法在NestJS这类Node服务端环境运行。服务端生成文件后要么写入本地磁盘,要么通过HTTP响应返回给客户端,完全用不上FileSaver
- 你当前的
zipFiles方法根本没有把读取到的CSV文件内容添加到JSZip实例中,就算解决了Blob报错,最终生成的也是空的压缩包
修复方法
JSZip在Node环境下生成文件时,要把输出类型改成nodebuffer,这个类型会返回Node原生支持的Buffer对象,不需要额外兼容。以下是两种常用场景的可运行代码:
场景1:服务端直接将zip保存到本地磁盘
你原来的文件读取方法只返回了文件名,没有读取文件内容,需要调整逻辑,在打包流程中读取文件内容加入压缩包:
const os = require('os'); const path = require('path'); const fs = require('fs/promises'); const JSZip = require('jszip'); async function readAllFilesInDirectory() { const userHomeDir = os.homedir(); // 注意此处原有路径写了多组../,建议打印tmpDir确认路径正确性,避免读错目录 const tmpDir = path.resolve(userHomeDir, '..', '../tmp'); console.log('读取目录路径:', tmpDir); try { const files = await fs.readdir(tmpDir); // 过滤后返回文件完整路径,方便后续读取内容 return files .filter((file) => { return !( file.startsWith('systemd') || file.startsWith('.') || file.startsWith('snap') || file.startsWith('ssh') || file.startsWith('tracker') || file.startsWith('vscode') ); }) .map(fileName => path.join(tmpDir, fileName)); } catch (e) { console.error('读取目录失败:', e); return []; } } async function zipFiles() { const allFilePaths = await this.readAllFilesInDirectory(); console.log('待打包文件列表:', allFilePaths); const zip = new JSZip(); // 逐个读取文件内容加入压缩包 for (const filePath of allFilePaths) { const fileName = path.basename(filePath); // 需求是打包CSV的话可以在这里加判断:if(!fileName.endsWith('.csv')) continue const fileContent = await fs.readFile(filePath); zip.file(fileName, fileContent); } // Node环境下用nodebuffer类型输出,不要用blob const zipBuffer = await zip.generateAsync({ type: 'nodebuffer', compression: 'DEFLATE' // 开启压缩减小体积 }); // 将zip写入本地文件 await fs.writeFile(path.join(__dirname, 'test.zip'), zipBuffer); console.log('压缩包生成成功'); }
场景2:NestJS接口返回zip给前端下载
如果是写接口让前端触发下载,不需要存本地,直接把Buffer返回给响应即可,示例:
import { Controller, Get, Res } from '@nestjs/common'; import { Response } from 'express'; // os、path、fs、JSZip、readAllFilesInDirectory方法的引入和上面保持一致即可 @Controller('export') export class ExportController { @Get('csv-zip') async downloadCsvZip(@Res() res: Response) { const allFilePaths = await this.readAllFilesInDirectory(); const zip = new JSZip(); for (const filePath of allFilePaths) { const fileName = path.basename(filePath); const fileContent = await fs.readFile(filePath); zip.file(fileName, fileContent); } const zipBuffer = await zip.generateAsync({ type: 'nodebuffer', compression: 'DEFLATE' }); // 设置响应头,告诉浏览器这是文件下载 res.set({ 'Content-Type': 'application/zip', 'Content-Disposition': 'attachment; filename="csv-files.zip"', 'Content-Length': zipBuffer.length }); res.end(zipBuffer); } }
提示:如果你确实需要在Node 16环境使用Blob类型(没有必要,Buffer兼容性更好),可以手动安装跨环境Blob实现包做全局挂载,但优先推荐直接使用nodebuffer方案,不需要额外依赖。
内容的提问来源于stack exchange,提问作者theplaceofburak
相关产品推荐
相关产品推荐

