Node16中fs.createReadStream报ENOENT错误的排查求助
本地创建文件后,使用以下代码读取:
fs.createReadStream(realFilePath)
路径通过path.join生成,代码如下:
import * as path from 'path'; // 其他代码 const realFilePath = path.join(__dirname, '..', '..', '..', nameFile); console.log('realFilePath', realFilePath); // 控制台输出示例:realFilePath /Users/myuser/Documents/projects_2/myProject/06_023_0aetc....pdf
控制台输出的路径完全正确,且文件确实存在于该路径下。在Node.js 12环境中此操作无任何问题,但升级到Node.js 16后,必须额外指定dirPath才能正常读取文件;否则程序会尝试读取当前文件所在目录,触发ENOENT文件不存在错误。
补充:按照建议添加检测代码后,控制台显示文件存在,但仍抛出ENOENT错误:
console.log('1) __dirname', __dirname); const realFilePath = path.join(__dirname, '..', '..', '..', nameFile); fs.access(realFilePath, fs.constants.F_OK, (err) => { console.log(`${realFilePath} ${err ? 'does not exist' : 'exists'}`); }); console.log('2) realFilePath', realFilePath);
检查ES模块下
__dirname的有效性:如果你的项目使用ES模块(package.json中配置了"type": "module"),Node.js 16中__dirname是未定义的。如果代码中直接使用了__dirname,可能是通过兼容方式获取的,但值可能不准确。建议手动计算正确的__dirname:import { fileURLToPath } from 'url'; import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename);替换异步检测为同步检测:
fs.access是异步方法,可能在它完成检测前,fs.createReadStream已经执行了。改用同步的fs.accessSync确保创建流前确认文件状态:try { fs.accessSync(realFilePath, fs.constants.R_OK); const stream = fs.createReadStream(realFilePath); // 后续流处理逻辑 } catch (err) { console.error('文件读取权限异常', err); }排查路径中的隐藏字符:控制台输出的路径看起来正常,但可能包含空格、不可见字符或编码问题。可以将路径转成十六进制检查:
console.log('路径十六进制:', Buffer.from(realFilePath).toString('hex'));改用
path.resolve生成绝对路径:path.resolve会自动解析成标准绝对路径,避免层级跳转可能带来的解析问题:const realFilePath = path.resolve(__dirname, '../../..', nameFile);确认程序启动的工作目录:Node.js 16中程序启动的工作目录可能和Node.js 12不同,添加代码检查当前工作目录:
console.log('当前工作目录:', process.cwd());如果工作目录变化,可能间接影响路径解析逻辑。
检查文件读取权限:
fs.access检测F_OK仅确认文件存在,还需确认是否有读取权限,改用R_OK检测:fs.access(realFilePath, fs.constants.R_OK, (err) => { console.log(`${realFilePath} ${err ? '无读取权限' : '可读取'}`); });
内容的提问来源于stack exchange,提问作者pmiranda

