用 Python 临时文件安全替换 JSON 配置

举报
yd_237867988 发表于 2026/11/10 01:30:43 2026/11/10
【摘要】 使用同目录临时文件和 os.replace 写入 JSON,并验证序列化失败时的清理与原文件保护。

直接覆盖配置文件时,如果序列化失败或进程中断,原文件可能已经被截断。一个实用的做法是在目标目录先写临时文件,写完后再用 os.replace 替换目标。下面用 Python 标准库实现并验证这个过程。

实现

import json
import os
import tempfile
from pathlib import Path

def save_json(path, data):
    path = Path(path)
    temp_name = None
    try:
        with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', dir=path.parent, delete=False) as f:
            temp_name = f.name
            json.dump(data, f, ensure_ascii=False, indent=2)
            f.write('\n')
            f.flush()
            os.fsync(f.fileno())
        os.replace(temp_name, path)
        temp_name = None
    finally:
        if temp_name is not None:
            Path(temp_name).unlink(missing_ok=True)

if __name__ == '__main__':
    with tempfile.TemporaryDirectory() as d:
        target = Path(d) / 'config.json'
        save_json(target, {'name': '示例', 'count': 2})
        assert json.loads(target.read_text(encoding='utf-8')) == {'name': '示例', 'count': 2}
        try:
            save_json(target, {'bad': object()})
        except TypeError:
            pass
        assert json.loads(target.read_text(encoding='utf-8'))['count'] == 2
        assert list(Path(d).iterdir()) == [target]
        print('写入和失败清理检查通过')

这里将临时文件放在目标文件同一目录,避免跨文件系统替换失败。json.dump 使用 UTF-8 和 ensure_ascii=False,便于查看中文;flush 和 fsync 将文件内容提交给操作系统,再关闭文件并替换。finally 在序列化等步骤失败时删除临时文件。目标父目录需要事先存在,写入权限也要足够。

验证

在临时目录执行脚本,先写入并读回字典,再故意传入不能 JSON 序列化的 object。第二次写入抛出 TypeError 后检查原数据仍存在、目录没有残留临时文件。本地 Python 运行输出“写入和失败清理检查通过”。这个测试覆盖序列化失败,不代表已经模拟断电或所有文件系统故障。

边界

os.replace 的替换语义依赖底层文件系统;同一文件系统上的原子替换可以减少读者看到半份文件的风险,但并不自动解决多进程并发写入,最后一个替换者仍可能覆盖其他更新。若需要严格的崩溃持久性,还应考虑目录项同步、平台差异和实际存储设备的行为。对于多用户并发配置,使用锁或事务型数据库更合适。临时文件的权限也可能与原文件不同,需要按应用需求明确设置。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。