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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 10:25:22