优先选gh_mirrors/sft/sftp——它轻量薄封装,适合嵌入微服务做sftp接入点,但需自行处理ssh连接、用户认证和权限控制;而pkg/sftp更稳,适配客户端场景。

直接用 sftp 包还是选 gh_mirrors/sft/sftp?
别自己封装底层 SSH 连接,golang.org/x/crypto/ssh + github.com/pkg/sftp 是最稳的组合。但如果你要快速启动一个轻量服务端(比如嵌入已有服务做文件上传入口),gh_mirrors/sft/sftp 更合适——它把 NewServer 封装得足够薄,不带 Web UI、用户管理或存储抽象层,适合微服务场景。
常见错误是误以为 gh_mirrors/sft/sftp 能直接当完整 SFTP 服务器跑起来:它不处理用户认证逻辑,也不监听 TCP 端口,必须你自己先建立 ssh.ServerConn,再传给 sftp.NewServer()。
- 要用
pkg/sftp做客户端(如从远端拉文件)→ 选它 - 要在已有 HTTP 服务里加个 SFTP 接入点(比如只允许某类设备连进来传日志)→ 用
gh_mirrors/sft/sftp - 想开箱即用、带 Web 管理、多协议、配额控制 → 直接上
sftpgo,别手写
sftp.NewServer() 启动后为什么客户端连不上?
90% 是权限和路径问题。SFTP 服务端本身不校验用户身份,它依赖上游 SSH 连接完成认证;而 sftp.NewServer() 只负责协议解析,不自动设置 chroot 或目录权限。
典型现象:Connection closed by remote host 或 Received message too long,其实是 SSH 层已断开,SFTP 根本没开始握手。
- 确保传入的
ssh.Conn已通过ssh.Handshake(),且ssh.User()返回非空字符串 -
ChrootDirectory必须由你手动创建,属主为root:root,权限为755;用户家目录(如/data/uploads/user1)属主为该用户,权限至少700 - 不要在
sftp.Server启动后才去改目录权限——连接建立时就检查,失败即拒
如何限制单个连接的上传大小和超时?
sftp.Server 本身不提供限流 API,得靠外层控制。真正生效的是 SSH 连接的 net.Conn 和 ssh.Connection 的配置。
比如防止大文件耗尽内存:不能等文件写到磁盘才拦截,得在 SFTP 协议层拦截 SSH_FXP_WRITE 请求。
- 用
sftp.ServerOption注册自定义FileHandler,在OpenFile时检查flags是否含os.O_CREATE,然后读取客户端发来的SSH_FXP_OPEN中的attrs(可能含预估大小) - 更可靠的做法是在
ssh.ServerConfig里设MaxPacketSize(默认 32KB),并配合SetDeadline()控制整个连接生命周期 - 注意:SFTP 协议本身不带“上传大小声明”,客户端是否发送
SIZE属性取决于实现(OpenSSH 不发,FileZilla 可能发),不能依赖
为什么并发上传时出现 EOF 或 broken pipe?
不是 goroutine 没加锁,而是底层 ssh.Channel 的读写是非线程安全的。每个 SFTP 连接对应一个 ssh.Channel,但多个 SFTP 请求(如同时 open/write/close)会复用同一个 channel。
gh_mirrors/sft/sftp 的 server.go 内部用了 sync.Mutex 保护 channel 写入,但如果你自己包装了 handler 并在多个 goroutine 里调用 channel.Write(),就会冲突。
- 所有对
ssh.Channel的Write()/SendRequest()调用,必须串行 —— 最好在 handler 入口加mutex.Lock() - 不要在 handler 里起 goroutine 异步写文件再回调 channel;SFTP 是同步协议,响应必须按请求顺序返回
- 测试时用
sftp命令行比 GUI 客户端更易复现问题,因为后者常做连接池和重试,掩盖了底层竞争
真正难调试的点在于:错误出现在 SSH 层,堆栈里看不到 SFTP 代码;得用 ssh.Config.Debug 打开底层日志,才能确认是 channel 关闭早于 SFTP 响应。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











