Node.js中使用fs模块读取和解析JSON文件的代码示例

作者:岩茶

时间:2026-10-30

浏览:386

本文详细讲解在Node.js中如何使用内置fs模块读取和解析JSON文件,涵盖异步Promise、同步读取、流式处理以及异常捕获等完整代码示例与注意事项。

文章目录

在Node.js应用开发中,读取并解析本地JSON文件是非常高频的基础操作,例如读取配置文件、读取本地模拟数据或解析缓存文件等。实现这个需求的核心路径非常清晰:先通过Node.js原生内置的fs文件系统模块将文件内容读取为字符串或Buffer缓冲区,然后再使用JavaScript内置的全局对象JSON.parse()将其反序列化为内存中的JavaScript原生对象。

一、核心原理与关键处理流程

Node.js的fs模块本身并不直接提供名为readJson的高级方法,因为文件读取属于I/O操作层,而格式转换属于数据序列化层。两者各司其职,标准流程可以清晰地划分为四个阶段:定位文件路径、读取原始文件内容、调用JSON反序列化、并在全程包裹异常防护机制。

Node.js中使用fs模块读取和解析JSON文件的代码示例
图1:Node.js使用fs读取与解析JSON的标准全流程架构

二、准备测试数据文件

为了保证后续每组示例代码都能实际运行并得到预期的解析结果,我们在项目的根目录下创建一个名为 config.json 的文件,内容如下:

{
  "appName": "DocReader",
  "version": "1.0.0",
  "port": 8080,
  "features": ["auth", "logging", "caching"],
  "db": {
    "host": "127.0.0.1",
    "timeout": 3000
  }
}

三、推荐方案:异步读取(fs/promises 与 async/await)

在现代Node.js服务端开发中,最推荐的做法是使用基于Promise的异步文件接口(fs/promises),并搭配 async/await 语法。这种方式不会阻塞Node.js单线程事件循环,同时代码结构扁平直观。

1. 完整代码示例

// read-promise.js
const fs = require('fs/promises');
const path = require('path');

async function loadConfig() {
  // 使用 path.join 构造绝对路径,防止因为终端启动目录不同导致 ENOENT
  const filePath = path.join(__dirname, 'config.json');

  try {
    // 读取文件内容,指定 'utf-8' 获取文本字符串
    const fileContent = await fs.readFile(filePath, 'utf-8');
    
    // 解析 JSON 字符串
    const data = JSON.parse(fileContent);
    
    console.log('读取并解析成功:');
    console.log(`应用名称: ${data.appName}`);
    console.log(`运行端口: ${data.port}`);
    console.log(`启用特性: ${data.features.join(', ')}`);
    return data;
  } catch (error) {
    if (error.code === 'ENOENT') {
      console.error(`错误:找不到指定文件 -> ${filePath}`);
    } else if (error instanceof SyntaxError) {
      console.error(`错误:文件不是有效的JSON格式 -> ${error.message}`);
    } else {
      console.error(`未知读取错误: ${error.message}`);
    }
    throw error;
  }
}

loadConfig();

2. 运行与输出示例

在终端中执行命令 node read-promise.js,预期控制台输出如下:

读取并解析成功:
应用名称: DocReader
运行端口: 8080
启用特性: auth, logging, caching

注意:如果省略了 'utf-8' 编码参数,fs.readFile() 默认返回的是一个 Buffer 字节缓冲区对象。虽然 JSON.parse(buffer.toString()) 同样有效,甚至现代V8引擎下 JSON.parse(buffer) 能隐式调用toString,但显式声明编码语义更加清晰,也能避免非标准环境下的潜在转换问题。

四、操作步骤:如何从零完成一次读取与解析

对于刚接触Node.js的新手,可以严格按照以下四个步骤来编写自己的读取逻辑,确保每一步都能得到明确的输入与输出结果:

Node.js中使用fs模块读取和解析JSON文件的代码示例
图2:Node.js中读取和解析JSON的实施步骤流程图

五、备用方案:同步读取(fs.readFileSync)

虽然异步是Node.js的核心优势,但在某些特定场景下,同步读取更为合适。例如:CLI命令行工具启动、项目初始化加载配置(服务监听端口前必须拿到参数),或者只执行一次的脚本。

1. 完整代码示例

// read-sync.js
const fs = require('fs');
const path = require('path');

function loadConfigSync() {
  const filePath = path.join(__dirname, 'config.json');

  try {
    // 同步读取文件,执行期间整个进程将同步等待磁盘I/O返回
    const rawContent = fs.readFileSync(filePath, 'utf-8');
    const data = JSON.parse(rawContent);

    console.log('同步读取并解析成功:');
    console.log('数据库配置:', data.db);
    return data;
  } catch (error) {
    console.error('同步读取失败:', error.message);
    // 启动阶段遇到关键配置缺失,通常直接退出进程
    process.exit(1);
  }
}

const config = loadConfigSync();

2. 运行与输出示例

在终端执行 node read-sync.js,预期控制台输出如下:

同步读取并解析成功:
数据库配置: { host: '127.0.0.1', timeout: 3000 }

警惕:切勿在高并发的Web服务请求处理函数(如Express路由回调)中调用 fs.readFileSync。这会导致整个Node.js进程卡在I/O等待上,所有并发用户请求都将被排队阻塞,导致服务延迟急剧攀升。

六、经典方案:传统回调函数(fs.readFile)

在维护较老的Node.js项目或旧版库时,你可能会遇到传统的“错误优先”(Error-First)回调函数写法。其核心逻辑完全相同,只是通过回调函数传递 (err, data):

// read-callback.js
const fs = require('fs');
const path = require('path');

const filePath = path.join(__dirname, 'config.json');

fs.readFile(filePath, 'utf-8', (err, rawData) => {
  if (err) {
    console.error('文件读取失败:', err.message);
    return;
  }

  try {
    const data = JSON.parse(rawData);
    console.log('回调模式解析成功,版本号:', data.version);
  } catch (parseErr) {
    console.error('JSON格式解析失败:', parseErr.message);
  }
});

七、两种主要读取方式的选择依据与特性对比

面对不同的业务场景,如何选用同步还是异步API?以下是清晰的选型对比:

Node.js中使用fs模块读取和解析JSON文件的代码示例
图3:Node.js中不同读取模式的对比决策图

八、必须注意的避坑指南

在实际工程中,仅仅将代码写出并不代表它足够健壮。以下是读取和解析JSON时最常碰到的四个隐患及应对措施:

1. 为什么不能直接使用 require('./config.json')?

在Node.js中,require 确实可以直接加载并自动解析JSON文件,但它存在两个严重副作用:

  • 强内存缓存机制: require 会将解析结果缓存在 require.cache 中。如果外部或运维动态修改了 config.json,后续通过 require 再次读取依然是旧数据,除非手动清空缓存。
  • 同步阻塞与内存常驻: require 是同步执行的,且数据长久驻留内存中,不适合大文件或动态文件。因此,动态数据与运行时配置应当始终使用 fs 模块读取。

2. 路径拼装问题(避免使用相对路径)

直接写 fs.readFile('./config.json') 依赖于 Node.js 启动时的当前工作目录(即 process.cwd())。如果你在项目的不同子目录中执行启动脚本,相对路径会发生漂移,抛出 ENOENT 错误。务必始终搭配 path.join(__dirname, '文件名') 形成绝对路径。

3. 文件包含 UTF-8 BOM 导致解析崩溃

在 Windows 环境下使用记事本等编辑器保存的 JSON 文件可能带有 UTF-8 BOM(字节顺序标记,字符编码 \uFEFF)。JSON.parse() 遇到带有 BOM 头的内容会直接抛出 SyntaxError: Unexpected token in JSON at position 0。解决方案是在解析前剔除 BOM:

function parseJsonWithoutBOM(content) {
  if (content.charCodeAt(0) === 0xFEFF) {
    content = content.slice(1);
  }
  return JSON.parse(content);
}

4. 异常精细化分类

不能简单地用一个空 catch 吞掉错误。通常需要明确区分是“文件不存在”(I/O层错误)还是“JSON语法损坏”(解析层错误),以便记录详细日志或向用户提供清晰的修复提示。

九、全景总结与最佳实践对照

为了帮助你在不同业务开发阶段快速查阅,下表总结了各种读取方式的适用场景与安全规范:

实现方式 I/O 特性 缓存行为 首选场景
fs/promises.readFile 非阻塞异步 (Promise) 无缓存,每次实时读取 生产级 Web 应用、API 接口、动态配置加载
fs.readFileSync 阻塞当前主线程 无缓存,每次实时读取 应用启动阶段、CLI 脚本、本地测试脚本
fs.readFile 非阻塞异步 (Callback) 无缓存,每次实时读取 兼容旧版 Node.js 项目与历史维护模块
require('./file.json') 同步加载并自动反序列化 永久缓存在 require.cache 静态常量定义、固定的元数据字典
Node.js中使用fs模块读取和解析JSON文件的代码示例
图4:Node.js处理JSON文件的四项核心实践原则总结

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章