Python 用 tempfile 安全创建临时文件:NamedTemporaryFile、TemporaryDirectory 与别自己拼 /tmp 的坑

Python 用 tempfile 安全创建临时文件:NamedTemporaryFile、TemporaryDirectory 与别自己拼 /tmp 的坑

你需要在程序里存一个临时文件------下载一个文件处理完就删、把上传内容落地校验、生成个中间产物给子进程读。很多人第一反应是这么写:

python 复制代码
import os

tmp_path = "/tmp/upload.dat"          # 自己拼一个路径
with open(tmp_path, "wb") as f:
    f.write(data)
process(tmp_path)
os.remove(tmp_path)                   # 记得删

这段代码有一堆问题:路径写死在 /tmp(Windows 上根本没这目录);文件名固定,两个进程同时跑会互相覆盖 ;更严重的是安全漏洞 ------攻击者可以预先在 /tmp/upload.dat 放一个符号链接指向 /etc/passwd,你的写入就被劫持了(这类叫「不安全临时文件」漏洞,CVE 里一抓一把)。标准库的 tempfile 模块就是来根治这些的。

NamedTemporaryFile:自动生成唯一名 + 自动删除

最常用的是 NamedTemporaryFile,它帮你在系统临时目录里创建一个名字随机、不会冲突的文件,还能在关闭时自动删掉:

python 复制代码
import tempfile

# 默认二进制模式,退出 with 块时自动删除
with tempfile.NamedTemporaryFile(suffix=".dat") as tmp:
    tmp.write(b"hello temp")
    tmp.flush()          # 写完刷盘,别的进程/函数才读得到完整内容
    print(tmp.name)      # 例如 /tmp/tmp8x3k_a9z.dat,名字是随机的
    process(tmp.name)
# 出了 with,文件已被自动删除,不用手动 os.remove

它的核心优势:

  • 文件名随机唯一:用的是安全的随机算法生成后缀前的随机串,天然避免多进程/多线程撞名。
  • 原子创建 :底层用 O_CREAT | O_EXCL 打开,如果同名文件已存在会失败而不是覆盖,从根上挡掉符号链接攻击。
  • 自动清理 :with 块结束(或对象被回收)时自动删除,不会因为你忘了 os.remove 或中途抛异常而留下垃圾文件。

几个常用参数:

python 复制代码
tempfile.NamedTemporaryFile(
    mode="w+",          # 文本模式;默认是 "w+b" 二进制
    suffix=".log",      # 文件名后缀,方便按扩展名识别
    prefix="myapp_",    # 文件名前缀
    dir="/data/tmp",    # 指定临时目录,不传则用系统默认
    delete=True,        # 关闭时是否自动删除,默认 True
)

一个高频坑:delete=True 时,别的程序在 Windows 上打不开

NamedTemporaryFile 有个跨平台陷阱。在 Linux 上,一个已打开的文件其他进程还能再打开;但在 Windows 上,delete=True 的临时文件被独占锁定,其他进程(甚至同进程里再 open 一次)会打不开 ,报 PermissionError

典型翻车场景:你想把内容写进临时文件,然后把路径交给外部命令去读:

python 复制代码
# ❌ 在 Windows 上会失败
with tempfile.NamedTemporaryFile(suffix=".txt") as tmp:
    tmp.write(b"data")
    tmp.flush()
    subprocess.run(["some_tool", tmp.name])  # Windows: PermissionError,文件被独占

Python 3.12 起 NamedTemporaryFile 加了 delete_on_close 参数专治这个;3.12 以前的通用写法是 delete=False + 手动清理:

python 复制代码
import os, tempfile, subprocess

# ✅ 跨版本通用:先关掉句柄,让外部程序能读,最后 finally 删
tmp = tempfile.NamedTemporaryFile(suffix=".txt", delete=False)
try:
    tmp.write(b"data")
    tmp.close()                              # 先关,释放独占
    subprocess.run(["some_tool", tmp.name])  # 外部程序现在能读了
finally:
    os.unlink(tmp.name)                      # 自己负责删除

记住这条经验:只要临时文件的路径需要交给「当前打开它的句柄之外」的第二方去读(外部进程、另一个 open),就别依赖 delete=True ,改成 delete=False 手动删,或用 3.12 的 delete_on_close=False

只要目录不要文件名:TemporaryDirectory

有时你需要的是一整个临时目录 ------解压一个压缩包、生成一批中间文件、给子进程一个工作区。TemporaryDirectory 会创建一个唯一目录,退出时连里面所有内容一起递归删除:

python 复制代码
import tempfile, os

with tempfile.TemporaryDirectory(prefix="build_") as tmpdir:
    print(tmpdir)  # /tmp/build_k3j9x1/,一个空目录
    # 在里面随便建文件
    for i in range(3):
        with open(os.path.join(tmpdir, f"part_{i}.txt"), "w") as f:
            f.write(f"chunk {i}")
    do_something_with(tmpdir)
# 出了 with,整个目录连同里面的文件全部被删除

比自己 os.makedirs("/tmp/mydir") + shutil.rmtree 强在:名字唯一不冲突、异常安全(哪怕中间抛错也保证清理)、跨平台。处理「一批临时产物」时优先用它,而不是攒一堆 NamedTemporaryFile

需要低层控制:mkstemp 返回文件描述符

NamedTemporaryFile 会替你管理生命周期,但如果你想完全自己掌控 (比如文件要长期存在、跨函数传递),用 mkstemp。它返回一个 (文件描述符, 路径) 元组,不自动删除,由你负责:

python 复制代码
import os, tempfile

fd, path = tempfile.mkstemp(suffix=".db", prefix="cache_")
try:
    # fd 是底层文件描述符,用 os.write 或包成文件对象
    with os.fdopen(fd, "w") as f:  # fdopen 接管 fd,关闭时会一起关
        f.write("persistent-ish data")
    # path 可以传给别的模块,文件不会被自动删
    print("生成于:", path)
finally:
    os.unlink(path)  # 生命周期完全由你管

mkstempNamedTemporaryFile 一样是安全创建 (O_EXCL 原子性),区别只在「谁管生命周期」:要自动清理选 NamedTemporaryFile,要手动掌控选 mkstemp。注意 os.fdopen(fd) 会接管这个 fd,所以别再单独 os.close(fd),否则会 double close 报错。

顺带一提:临时目录到底在哪,gettempdir

系统临时目录不一定是 /tmptempfile 会按顺序检查环境变量 TMPDIRTEMPTMP,再退回平台默认。想知道当前用的是哪,或想统一设置,用:

python 复制代码
import tempfile

print(tempfile.gettempdir())  # 当前临时目录,如 /tmp 或 C:\Users\xxx\AppData\Local\Temp
# 想全局改临时目录(比如 /tmp 空间不够,想用大盘):
tempfile.tempdir = "/data/bigdisk/tmp"

这也解释了为什么别把路径写死成 /tmp :容器里 /tmp 可能很小甚至只读,而 tempfile 会尊重 TMPDIR 环境变量,让运维能灵活重定向,你的代码一个字都不用改。

小结

  • 永远别自己拼 /tmp/固定名 这种临时路径------有覆盖冲突和符号链接劫持的安全风险,还不跨平台。
  • NamedTemporaryFile :自动生成唯一名、原子安全创建、with 结束自动删,日常首选。
  • Windows 坑 :delete=True 会独占文件,路径要给外部进程读时改用 delete=False 手动删,或 3.12 的 delete_on_close=False
  • TemporaryDirectory:要一整个临时工作区、一批中间文件时用它,退出自动递归清理。
  • mkstemp :要自己完全掌控生命周期时用,返回 (fd, path),记得 os.fdopen 接管 fd 后别重复 close。
  • 一句话记忆点 :临时文件交给 tempfile,唯一命名 + 原子创建 + 自动清理三件事它全包了,你只管写业务。
相关推荐
lvshuocool1 小时前
golang 多版本管理工具 -- g
开发语言·后端·golang
matlab代码1 小时前
基于matlab数字图像处理的指纹识别系统 点线特征(GUI界面)【源码69期】
开发语言·matlab
linx2951 小时前
单元三 · 那 C 的底层知识怎么办
c语言·开发语言·数据结构·c++·嵌入式硬件
北城bot1 小时前
把 Python 脚本打包成 exe
python
MetaLite1 小时前
Java 时间处理的两个坑:单位误判,半秒算成一秒
java·开发语言·前端
数据知道1 小时前
国密算法实战——SM2/SM3/SM4 在国产系统中的应用
网络·算法·安全·网络安全·密码学·哈希算法
萧鼎2 小时前
Python 数据验证神器 Pydantic:数据解析与验证、设置管理、JSONSche、与ORM集成全搞定
开发语言·python
jimmyleeee2 小时前
大模型安全之十一:拆解现代 AI 系统的“骨架”:一份安全架构的完整蓝图
人工智能·安全·安全架构
haon11222 小时前
海外信贷的策略全生命周期:从准入到还款的本地化布防
大数据·开发语言·深度学习·面试·职场和发展·产品经理