临时文件必须显式清理,Go中os.CreateTemp和os.MkdirTemp不自动删除;测试用t.Cleanup最可靠,生产环境需信号捕获+路径跟踪+os.RemoveAll安全清理。

临时文件必须显式清理,没有“自动销毁”这回事
Go 语言里 os.CreateTemp 和 os.MkdirTemp 只负责创建,不负责删除。哪怕你只在 main 函数里调一次,不手动删,文件就永远留在磁盘上。这不是 bug,是设计——Go 不替你做生命周期决策。
-
defer os.Remove(f.Name())看似省事,但遇到os.Exit(1)、panic 未被捕获、或 goroutine 提前退出时,根本不会执行 - 测试中用
t.Cleanup是唯一能覆盖 panic/失败/成功的可靠方式;生产环境得靠信号捕获 + 路径跟踪 + 主动调用os.RemoveAll - 别信“系统会定期清/tmp”——容器里
/tmp可能是 tmpfs,重启即丢;但宿主机或长期运行的服务,残留文件只会越积越多
os.Remove 和 os.RemoveAll 别混用,传错参数直接报错
os.Remove 只能删单个文件或空目录;传一个非空目录进去,立刻返回 directory not empty 错误。而 os.RemoveAll 才是删临时目录的正确选择,它递归清空整个子树。
- 删单个临时文件:
os.Remove(tmpFile.Name()) - 删临时目录(含所有子文件/子目录):
os.RemoveAll(dir),别试图先os.Remove(dir)再逐个删里面的东西 -
os.RemoveAll不校验路径安全性:传"../"或"../../etc"会真实执行,不是理论风险——务必先用filepath.Abs(dir)转绝对路径,再检查是否以os.TempDir()开头
测试里用 t.Cleanup 是底线,别靠 defer 或 TestMain 统一清理
测试中漏掉清理最常见原因,就是把 defer os.RemoveAll(dir) 写在 TestMain 里,或者依赖函数级 defer。前者在子测试 panic 时不触发,后者在 t.Fatal 后仍继续执行、但 t.FailNow() 会跳过所有 defer。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 每个测试用例独立创建临时资源:用
os.MkdirTemp("", "test-*")或os.CreateTemp("", "file-*.bin") - 立刻注册
t.Cleanup(func() { os.RemoveAll(dir) }),它不依赖函数返回,panic、失败、成功都会触发 - Go 1.16+ 直接用
t.TempDir()更省心,它内部已绑定t.Cleanup,返回路径可直接用 - 并发测试(
t.Run)必须为每个子测试单独建目录,别共用一个os.MkdirTemp结果,否则命名冲突或清理错乱
生产环境不能靠 defer,得用信号捕获 + 路径登记
长期服务里,defer 基本失效:进程收到 SIGTERM、被 os.Exit() 中断、或 panic 未恢复,defer 全部跳过。真正可行的是启动时注册信号监听,把所有临时路径存进全局 map/slice,退出前统一清理。
立即学习“go语言免费学习笔记(深入)”;
- 启动时调用
signal.Notify(c, os.Interrupt, syscall.SIGTERM),另起 goroutine 等待信号 - 所有临时路径(
os.CreateTemp或os.MkdirTemp返回的)存入sync.Map或带锁 slice - 收到信号后,遍历路径列表,对每个路径调用
os.RemoveAll;Windows 下若遇syscall.ERROR_SHARING_VIOLATION,最多重试 2 次,每次time.Sleep(100 * time.Millisecond) - 清理前务必
filepath.Abs+ 前缀校验,且绝不清理os.TempDir()本身,只清自己创建的子目录
真正麻烦的从来不是“怎么删”,而是“删之前没确认路径是否安全”和“删的时候文件正被其他进程占用”。这两点踩中一个,轻则清理失败,重则误删系统文件。

















