go标准库无ftp服务端,net/ftp仅含客户端;需用go-ftp-server等第三方库或vsftpd等成熟服务,注意pasvaddress、portrange、filesystem权限及路径相对性。

Go 标准库根本不能跑 FTP 服务器
别试了——net/ftp 只有客户端,没有 Server、没有 ListenAndServe、也没有任何服务端类型。你 import 它之后写 ftp.NewServer() 或 ftp.Listen(":21"),编译直接报错:undefined: ftp.NewServer。
FTP 协议本身是双连接(控制 + 数据)、分主动/被动模式、还要处理用户认证和文件系统权限,标准库压根不碰这事。硬啃 RFC 959 写完整服务端,工作量远超“简单实现”范畴。
- 常见错误现象:
import "net/ftp"后以为能起服务,结果连编译都过不去 - 真实使用场景:CI 中临时传构建产物、内网小工具配套、测试环境模拟 FTP 端点
- 替代方案只有两个:用第三方服务端库,或改用现成的
vsftpd/pure-ftpd,Go 只做配用户、启停、审计等外围控制
想快速起一个可连的 FTP 服务端,用 github.com/freddierice/go-ftp-server
这是目前最轻量、纯 Go、无 cgo、接口清晰的服务端库。3 行代码就能跑起来,FileZilla/curl/ftp 命令都能连。
关键不是“怎么写”,而是“怎么配对”:它要求你提供一个 ftp.FileSystem 实现,且默认的 os.DirFS("/tmp") 是只读的——上传(STOR)和建目录(MKD)必然失败。
- 必须显式设置
PasvAddress,比如"127.0.0.1",否则 PASV 模式下客户端连不上数据端口 -
PortRange要设成一段可用端口,如[50000, 50010],避免被系统占用 - 非 root 用户启动时,端口得 >1024,比如用
":2121",别硬占":21" -
Open返回的io.ReadWriteCloser必须支持Write和Seek(上传中断续传依赖),os.OpenFile要带os.O_CREATE | os.O_WRONLY | os.O_TRUNC
go get github.com/jlaffaye/ftp 是当前最稳的 FTP 客户端选择
它不依赖 cgo,跨平台好,API 直观,Login/Stor/Retr 都有,比手动拼 USER/PASS/STOR 命令靠谱得多。
Colly 是一个用于 Go 语言的快速开源爬取和爬虫框架。它适用于从简单的页面提取到异步爬虫处理大量页面集合,支持请求回调和结构化解析。
但几乎所有上传失败,都不是库的问题,而是你没调对路径、没设超时、没处理被动模式防火墙拦截。
-
Stor("remotefile.txt", file)的远程路径是**相对于登录后根目录的路径**,不是绝对路径;传"/upload/a.txt"会失败,得传"upload/a.txt" - 目标目录必须已存在,
Stor不自动建父目录;先MakeDir("upload")再ChangeDir("upload") - 默认无超时,大文件上传卡住不报错;务必用
ftp.DialTimeout或包装net.DialTimeout - 中文文件名在部分服务器上乱码,优先用 ASCII 测试;二进制文件必须设
conn.SetType(ftp.Binary)
生产环境别硬扛 FTP 协议栈
FTP 的 PASV 模式在 Docker、K8s、云主机上天然受限:数据端口随机、需额外开防火墙、NAT 回包失败。你没法靠改几个配置项修好,因为协议层就决定了它不适合现代网络拓扑。
更务实的做法是:本地开发/测试用 go-ftp-server 或 ftpd 快速验证;上线后换成熟 FTP 服务(vsftpd 或 pure-ftpd),用 Go 写控制逻辑——比如调 shell 命令生成用户、读取数据库配权限、记录传输日志、触发 webhook。
真正容易被忽略的,是权限校验那层薄薄的抽象:FileSystem 接口里 Stat 返回的 os.FileInfo.Mode() 如果没设对(比如该目录返回 0755,该文件返回 0644),macOS Finder 或某些旧版客户端会直接不显示条目,连错误都不报。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










