Go临时文件需确保任何异常下均被清理:defer在os.Exit、多return、goroutine中失效,应结合nil检查、清理列表、context统一回收;pattern必须含*防冲突;写入失败或panic时仍需清理;Docker中避免/tmp内存溢出;路径校验防遍历。

Go 的临时文件不会自动销毁,所谓“高性能”不是靠省掉清理动作,而是让清理逻辑不拖慢主流程、不漏掉任何退出路径、不因 panic 或 os.Exit 失效。
os.CreateTemp 后必须立即绑定清理逻辑,但 defer 不够用
很多人写 defer os.Remove(f.Name()) 就以为万事大吉,实际在这些场景会彻底失效:
-
os.Exit(1)—— 所有 defer 被跳过,文件永远残留 - 函数内多处
return,只在开头 defer,后续 error 分支没覆盖 - goroutine 中创建临时文件,主 goroutine 退出后子 goroutine 还在跑,defer 永不执行
- panic 发生时 defer 会执行,但若 panic 前已调用
os.Exit,依然不触发
推荐做法:对短生命周期(如单次 HTTP 请求处理),仍可用 defer,但要包裹成匿名函数并检查文件是否已赋值;对长生命周期或跨 goroutine 场景,必须维护一个可追踪的清理列表,配合 context 或信号监听统一回收。
pattern 必须含 *,否则并发下必然冲突
这是高频踩坑点:os.CreateTemp("", "log") 生成的永远是 log,第二次调用直接返回 os.ErrExist;而 os.CreateTemp("", "log-*.txt") 才会替换 * 为随机字符串(如 log-a1b2c3.txt)。
立即学习“go语言免费学习笔记(深入)”;
- 硬编码时间戳(如
"log-" + strconv.FormatInt(time.Now().UnixNano(), 10) + ".txt")不可靠 —— 纳秒级在高并发下极易重复,且无原子性保障 - Windows 下若 pattern 不含
*,os.CreateTemp可能静默失败或返回非预期错误 - 建议固定格式:前缀 +
-*.ext,例如"upload-*.bin"、"cache-*.json"
写入失败时文件仍残留,清理必须覆盖所有退出路径
常见错误是只在 os.CreateTemp 成功后 defer 删除,但后续 f.Write() 或 f.Sync() 失败,文件已创建却没人删。
- 正确写法是声明文件变量为 nil,defer 中判空再删:
tmp, err := os.CreateTemp("", "job-*.bin"); if err != nil { return err }; defer func() { if tmp != nil { os.Remove(tmp.Name()) } }() - 如果文件需传递给其他 goroutine,不能依赖 defer,应改用带引用计数或超时机制的封装结构
- 写入中途 panic 时,defer 会执行,但若
os.Remove自身失败(如权限不足、文件被占用),错误会被吞掉 —— 生产环境建议记录该类清理失败日志
大文件 + Docker 场景要防 OOM 和路径越界
临时文件体积和存放位置直接影响稳定性:
- Docker 容器中
/tmp往往是 tmpfs(内存文件系统),写入几百 MB 临时文件可能直接触发 OOM kill - 不要硬写
/tmp/myapp/,应传""让os.TempDir()自动选目录,它会尊重TMPDIR环境变量 - 用
os.RemoveAll清理临时目录时,必须先做路径校验:filepath.Abs(dir)→ 检查是否以os.TempDir()返回值开头 → 排除../类路径遍历风险 -
os.Remove对非空目录一定失败,必须用os.RemoveAll;但它不校验路径安全性,误传".."可能删掉整个磁盘
真正难的不是创建临时文件,而是确保它在任何异常路径下都消失 —— 这需要把清理逻辑当成核心业务逻辑来设计,而不是收尾时随手加的一行 defer。



















