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

使用Node.js安全写入JSON对象属性的可靠方案有哪些?

问题根因

你遇到的JSON尾部追加异常绝大多数是异步写入并发冲突导致的:

  • 当前代码里的fs.promises.writeFile是异步操作,你没有用await等待写入完成,若短时间内多次触发同一个PACKAGE_FILE的写入逻辑,多个写入任务会并行执行,操作系统的文件写操作没有互斥,就会出现前一次写入的内容未完全覆盖、后一次写入的内容追加到前一次末尾的情况。
  • 小概率场景是读取JSON文件时没有等待读操作完成,就直接修改后写入,拿到的file对象本身就是不完整的。
安全实现方案

方案1:添加await控制串行执行,禁止并发写

先捋顺异步操作的时序,确保同一时间只有一个写操作执行:

// 先确保读取文件的操作已经完成
const file = JSON.parse(await fs.promises.readFile(PACKAGE_FILE, 'utf-8'))

// 修改属性
for (const [key, value] of Object.entries(dependencyFileTypeScript)) {
  file.devDependencies ??= {} // 加兜底逻辑,避免devDependencies不存在时报错
  file.devDependencies[key] = value
}

// 等待写入完成再执行后续操作
try {
  await fs.promises.writeFile(PACKAGE_FILE, JSON.stringify(file, null, 2))
} catch (err) {
  console.error('写入package.json失败:', err)
}

方案2:用原子写入替代普通writeFile

如果是生产环境频繁操作配置文件的场景,建议用原子写入逻辑,既可以避免写入中途进程崩溃导致文件损坏,也能天然避免并发冲突:

import { writeFile } from 'fs/promises'
import { tmpdir } from 'os'
import { join } from 'path'
import { renameSync } from 'fs'

async function atomicWriteJson(filePath, data, indent = 2) {
  // 先写入临时文件
  const tmpPath = join(tmpdir(), `${Date.now()}-${Math.random().toString(36).slice(2)}.json`)
  await writeFile(tmpPath, JSON.stringify(data, null, indent))
  // 用重命名操作原子替换原文件,同一文件系统下重命名是原子操作,不会出现半写状态
  renameSync(tmpPath, filePath)
}

// 调用示例
await atomicWriteJson(PACKAGE_FILE, file)

方案3:加文件锁避免多进程/多实例写入冲突

如果你的程序有多个进程都可能修改同一个JSON文件,需要添加文件互斥锁:

import lockfile from 'proper-lockfile'
import fs from 'fs/promises'

try {
  // 加锁
  const release = await lockfile.lock(PACKAGE_FILE)
  try {
    // 读文件→改属性→写文件的逻辑全部放在锁持有期间执行
    const file = JSON.parse(await fs.readFile(PACKAGE_FILE, 'utf-8'))
    Object.assign(file.devDependencies ?? {}, dependencyFileTypeScript)
    await fs.writeFile(PACKAGE_FILE, JSON.stringify(file, null, 2))
  } finally {
    // 释放锁
    await release()
  }
} catch (err) {
  console.error('操作失败:', err)
}

内容的提问来源于stack exchange,提问作者Aleksandrs

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 12:27:00