NestJS API遇ENOENT错误崩溃,如何捕获错误并返回请求?
解决NestJS文件下载路由中文件不存在导致服务崩溃的问题
你的问题出在createReadStream的错误无法被当前的try/catch捕获——因为它的错误是通过流的error事件触发的,而非同步抛出或Promise拒绝,所以原来的错误处理逻辑完全没起作用,最终导致未捕获的异常崩溃服务。
下面是两种可靠的解决方式:
方式一:先检查文件是否存在(推荐异步方式)
在创建读取流之前,先异步检查文件的可访问性,这样能提前捕获“文件不存在”的情况,同时避免阻塞事件循环:
import { createReadStream, access } from 'fs'; import { join } from 'path'; import { promisify } from 'util'; import { HttpException, HttpStatus } from '@nestjs/common'; // 把回调式的access转为Promise版本 const accessAsync = promisify(access); // 路由处理函数 async function downloadFile(res, filesrc) { const filePath = join(process.cwd(), filesrc.path); try { // 先检查文件是否可访问(存在且有权限) await accessAsync(filePath); const file = createReadStream(filePath); res.set({ "Content-Type": `${filesrc.type}`, "Content-Disposition": `attachment;filename="${filesrc.original_name}"`, "Content-Length": +filesrc.size }); res.status(HttpStatus.OK); return new StreamableFile(file); } catch (err) { // 判断是否是文件不存在的错误 if (err.code === 'ENOENT') { throw new HttpException('请求的文件不存在', HttpStatus.NOT_FOUND); } // 其他错误返回500 throw new HttpException('服务器内部错误', HttpStatus.INTERNAL_SERVER_ERROR); } }
方式二:给读取流绑定错误事件
如果不想提前检查文件,也可以直接给createReadStream返回的流绑定error事件,捕获流触发的错误,避免异常扩散导致服务崩溃:
import { createReadStream } from 'fs'; import { join } from 'path'; import { HttpException, HttpStatus } from '@nestjs/common'; // 路由处理函数 function downloadFile(res, filesrc) { const filePath = join(process.cwd(), filesrc.path); const file = createReadStream(filePath); // 绑定错误事件,捕获流的错误 file.on('error', (err) => { if (err.code === 'ENOENT') { res.status(HttpStatus.NOT_FOUND).send('请求的文件不存在'); } else { res.status(HttpStatus.INTERNAL_SERVER_ERROR).send('服务器内部错误'); } // 销毁流释放资源 file.destroy(); }); res.set({ "Content-Type": `${filesrc.type}`, "Content-Disposition": `attachment;filename="${filesrc.original_name}"`, "Content-Length": +filesrc.size }); res.status(HttpStatus.OK); return new StreamableFile(file); }
关键说明
- 必须处理流的
error事件:Node.js中如果流的error事件没有被监听,会直接抛出未捕获异常导致进程崩溃,这是你当前服务崩溃的核心原因。 - 优先用异步文件检查:
accessAsync是异步操作,不会阻塞事件循环,比同步的fs.existsSync更适合高并发场景。 - 区分错误类型:通过
err.code === 'ENOENT'精准判断“文件不存在”,返回合适的404状态码,而非统一返回500。
内容的提问来源于stack exchange,提问作者Renan Fernandes
相关产品推荐
相关产品推荐

