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

Node.js调用fs.writeFile写入JSON文件时格式随机损坏如何解决

问题诱因
  • 核心原因是并发写入冲突:你当前使用的异步readFile+writeFile逻辑是非原子操作,也没有加锁控制,当同一时间有多个请求/异步任务触发这段读写逻辑时,会出现多个任务先后读取到旧版文件内容、并行写入的情况,两次写入的内容部分覆盖/叠加,就会出现你看到的「完整JSON后拼接半截JSON」的损坏现象。
  • 次要原因是非原子写入风险:直接用writeFile覆盖原文件,写入过程中如果出现进程崩溃、系统断电、磁盘满等异常,会导致文件只写入一半内容直接损坏。
  • 你当前的writeFile回调为空,所有写入层面的错误都会被吞掉,无法及时感知异常。
修复方案
  • 加读写锁控制:单进程场景下可以使用proper-lockfile这类库,在读写database.json前先加锁,确保同一时间只有一个读写操作执行,从根源避免并发修改冲突。
  • 改用原子写入逻辑:不要直接覆盖原文件,先将序列化后的JSON写入同目录下的临时文件(例如database.json.tmp),确认临时文件写入成功后,再调用fs.rename原子替换原文件,即使写入临时文件过程中出现异常,原文件也不会被破坏。示例逻辑参考:
const fs = require('fs/promises')
const path = require('path')
const lockfile = require('proper-lockfile')

async function updateUserCooldown(user) {
  const dbPath = path.resolve(__dirname, database_name)
  const tmpPath = `${dbPath}.tmp`
  let release
  try {
    // 加锁
    release = await lockfile.lock(dbPath)
    // 读取文件
    const data = await fs.readFile(dbPath, 'utf-8')
    const obj = JSON.parse(data)
    const userIdx = obj.users.findIndex(v => v.id === user)
    const cooldowns = obj.users[userIdx].cooldowns
    if (!cooldowns.some(item => item.QUIZ)) {
      cooldowns.push({ "QUIZ": Date.now() + 86400000 })
    }
    const json = JSON.stringify(obj, null, 4)
    // 写入临时文件
    await fs.writeFile(tmpPath, json, 'utf-8')
    // 原子替换原文件
    await fs.rename(tmpPath, dbPath)
  } catch (err) {
    console.error('写入数据库失败', err)
    // 清理残留的临时文件
    await fs.unlink(tmpPath).catch(() => {})
  } finally {
    // 释放锁
    if (release) await release()
  }
}
  • 补全所有异步操作的错误捕获逻辑,不要留空回调,避免异常被吞。
  • 如果你的业务修改频率较高,建议直接改用SQLite这类轻量文件数据库,自带事务和并发控制能力,无需手动处理文件锁和原子写入的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 04:36:03