应监听目录而非单个文件,过滤Write/Create事件并校验文件后缀与os.Stat状态;转码逻辑需用带缓冲channel限流、goroutine异步执行,避免阻塞与资源失控。

如何用 Go 监听文件变化并触发转码逻辑
Go 本身不内置文件监听能力,得靠 fsnotify 库。但直接监听原始文件路径容易漏事件——比如编辑器先写临时文件再原子重命名,或日志轮转时旧文件被 mv 走。正确做法是监听目录而非单个文件,且需对 fsnotify.Event.Name 做后缀和状态双重过滤:
- 只处理
Write或Create事件,忽略Chmod - 检查
Event.Name是否匹配目标格式(如.mp4、.log),避免误触隐藏文件 - 用
os.Stat()确认文件已写完(大小稳定 + 修改时间不再变动),防止读到半截内容
转码命令执行时如何避免阻塞主线程和资源失控
调用 ffmpeg 或自研转码器必须开 goroutine,但不能无限制并发——尤其当多个文件同时落盘时,CPU 和内存会瞬间打满。关键控制点有三个:
- 用带缓冲的 channel 控制并发数,例如
sem := make(chan struct{}, 3),每次执行前sem ,结束后 <code><-sem - 给
exec.Command设置context.WithTimeout,超时强制 kill,防止卡死进程 - 转码输出路径必须基于输入路径生成(如
strings.Replace(input, ".mp4", "_transcoded.mp4", 1)),避免多文件写同一目标引发覆盖或冲突
传输失败时怎么保证不丢数据且可重试
网络传输(HTTP 上传、SFTP、Kafka)最怕中间断连导致转码成功但上传丢失。不能依赖“转码完立刻发”,得把传输任务持久化:
- 转码成功后,将目标文件路径 + 传输参数(URL、token、headers)写入本地 SQLite 或 BoltDB,作为待发送队列
- 另起 goroutine 轮询该队列,用指数退避(1s → 2s → 4s)重试失败项
- 传输成功后,用
os.Remove()清理本地转码文件;失败则保留,下次重试时跳过转码直接上传
为什么不用现成的流式转码库而选择调外部命令
Go 生态里没有成熟、低延迟、多 codec 支持的纯 Go 实时转码库。gstreamer-go 绑定复杂且跨平台编译麻烦,ffmpeg-go 本质还是封装 exec.Command。直接调 ffmpeg 更可控:
立即学习“go语言免费学习笔记(深入)”;
- 能精确传参(如
-c:v libx264 -preset fast -crf 23),避免抽象层屏蔽关键选项 - 错误信息直接来自 stderr,比封装后的 error message 更准(比如
Invalid data found when processing input) - 内存占用由 ffmpeg 自己管理,不会因 Go runtime GC 拖慢 IO 密集型任务
真正难的不是启动命令,而是把文件监听、转码、传输三阶段的状态对齐——比如传输失败后,要能准确知道是哪个文件卡在哪一步,日志里没留 Event.Name 和 cmd.Args 就很难排查。


















