你需要在程序里存一个临时文件——下载一个文件处理完就删、把上传内容落地校验、生成个中间产物给子进程读。很多人第一反应是这么写:
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,它帮你在系统临时目录里创建一个名字随机、不会冲突的文件,还能在关闭时自动删掉:
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或中途抛异常而留下垃圾文件。
几个常用参数:
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。
典型翻车场景:你想把内容写进临时文件,然后把路径交给外部命令去读:
# ❌ 在 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 + 手动清理:
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 会创建一个唯一目录,退出时连里面所有内容一起递归删除:
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。它返回一个 (文件描述符, 路径) 元组,不自动删除,由你负责:
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) # 生命周期完全由你管
mkstemp 和 namedtemporaryfile 一样是安全创建(o_excl 原子性),区别只在「谁管生命周期」:要自动清理选 namedtemporaryfile,要手动掌控选 mkstemp。注意 os.fdopen(fd) 会接管这个 fd,所以别再单独 os.close(fd),否则会 double close 报错。
顺带一提:临时目录到底在哪,gettempdir
系统临时目录不一定是 /tmp。tempfile 会按顺序检查环境变量 tmpdir、temp、tmp,再退回平台默认。想知道当前用的是哪,或想统一设置,用:
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,唯一命名 + 原子创建 + 自动清理三件事它全包了,你只管写业务。
以上就是python使用tempfile安全创建临时文件的方法的详细内容,更多关于python tempfile创建临时文件的资料请关注代码网其它相关文章!
发表评论