应使用 github.com/shirou/gopsutil/v3/disk 包统一获取磁盘剩余空间,传入挂载点路径(如 /tmp 或 C:),检查 Available 和 InodesFree,并预留至少 5% 缓冲,而非依赖 os.Stat。

Go 中如何获取磁盘剩余空间
Go 标准库没有直接提供跨平台的磁盘空间查询函数,得依赖 syscall.Statfs(Unix/Linux/macOS)或 syscall.GetDiskFreeSpaceEx(Windows)。但手动调用 syscall 容易出错、可读性差,更稳妥的做法是用社区验证过的包——golang.org/x/sys/unix(Unix 系统)或 golang.org/x/sys/windows(Windows),不过最省心的是统一用 github.com/shirou/gopsutil/v3/disk。
它封装了底层差异,返回结构体字段清晰:Available 是当前用户可用字节数(已扣除 root 预留空间),Total 和 Free 含义不同,别混淆。检查前务必传入目标文件所在**挂载点路径**(比如 /home),不是文件路径本身(/home/user/data.txt)。
示例:获取 /tmp 所在分区的可用空间:
import "github.com/shirou/gopsutil/v3/disk"
<p>func getAvailSpace(path string) (uint64, error) {
partitions, err := disk.Partitions(false)
if err != nil {
return 0, err
}
for <em>, p := range partitions {
if p.Mountpoint == path || strings.HasPrefix(path, p.Mountpoint+"/") {
usage, </em> := disk.Usage(p.Mountpoint)
return usage.Available, nil
}
}
return 0, fmt.Errorf("no mountpoint found for %s", path)
}写入前必须检查的两个关键条件
只查“剩余空间 > 文件大小”不够。真实场景中要同时确认:
立即学习“go语言免费学习笔记(深入)”;
-
Available足够容纳待写内容(含可能的临时缓冲、日志追加等额外开销) - 目标路径所在文件系统支持写入(比如只读挂载、权限不足、inode 耗尽)
尤其是 inode 耗尽——磁盘显示还有几十 GB,却报 no space left on device,十有八九是这个原因。gopsutil 的 disk.Usage 返回的 InodesFree 字段能帮你提前发现。
建议预留至少 5% 缓冲(尤其对日志轮转、数据库 WAL 等连续写入场景),避免刚写满就触发错误。计算时用 Available * 0.95 > expectedSize,而不是硬比 >。
常见错误:误用 os.Stat 或 os.IsNotExist
os.Stat 只能告诉你路径是否存在、是否为目录,完全不反映磁盘状态;os.IsNotExist 对“磁盘满”毫无意义——文件根本还没创建,错误发生在 os.Create 或 *os.File.Write 阶段。
典型错误链:
→ 先 os.Stat 检查父目录存在 → ✅
→ 再 os.Create 创建文件 → ? write /path/to/file: no space left on device
→ 程序 panic 或静默失败
正确顺序必须是:
1. 解析目标文件路径所属挂载点
2. 查询该挂载点 Available 和 InodesFree
3. 判断是否满足写入阈值
4. 最后才调用 os.Create 或 os.OpenFile
Windows 下要注意路径格式和权限
Windows 不以挂载点概念组织磁盘,而是驱动器盘符(C:, D:)。传给 disk.Usage 的路径必须是 C:\ 这种根路径,不能是 C:\Users —— 否则会返回错误或不准确数据。
另外,即使磁盘空间充足,若进程没获得该卷的写入权限(比如被组策略限制、UAC 拦截),仍会失败。此时 disk.Usage 查不到权限问题,需在 os.OpenFile 后检查 err 是否为 os.ErrPermission,并和磁盘空间检查逻辑分开处理。
小技巧:用 filepath.VolumeName 提取盘符,再拼成根路径:
vol := filepath.VolumeName(filePath)
if vol != "" {
root := vol + "\"
usage, _ := disk.Usage(root)
// ...
}真正麻烦的从来不是“怎么查空间”,而是“查完之后要不要拒绝写入”。业务逻辑里得明确:是直接返回错误,还是降级写到临时盘,或是压缩后再写?这些决策点比技术实现更关键,也更容易被忽略。


















