如何在 Go 中实现 FTP 文件上传并实时监控上传进度

如何在 Go 中实现 FTP 文件上传并实时监控上传进度

本文介绍如何使用 go 语言通过 ftp 协议上传文件的同时安全、可靠地获取文件状态(如是否存在、大小等),重点解决因共享 ftp 连接导致的并发阻塞与竞态问题,并提供双连接方案与同步优化实践。

本文介绍如何使用 go 语言通过 ftp 协议上传文件的同时安全、可靠地获取文件状态(如是否存在、大小等),重点解决因共享 ftp 连接导致的并发阻塞与竞态问题,并提供双连接方案与同步优化实践。

在 Go 中使用 github.com/dutchcoders/goftp 库进行 FTP 文件上传时,若尝试在上传过程中(如调用 ftp.Stor())并发执行 ftp.List() 查询文件状态,极易因复用同一 FTP 连接实例而引发阻塞或竞态——因为该库的底层连接并非线程安全,且 FTP 协议本身要求命令串行执行(控制通道独占)。正如示例代码所示:两个 goroutine 共享 *goftp.FTP 实例,Stor() 与 List() 会相互抢占控制连接,导致上传挂起、查询返回空列表,甚至出现不可预测的行为(如日志中 7 <nil> 表明 Stor 返回 nil 错误但未显式报错,实为连接被另一 goroutine 中断)。

✅ 正确做法是 为并发操作分配独立的 FTP 连接实例

// 创建两个独立连接
ftpUpload, err := goftp.Connect("serverip:port")
if err != nil {
    log.Fatal("upload connect failed:", err)
}
defer ftpUpload.Close()

ftpMonitor, err := goftp.Connect("serverip:port") // 第二个独立连接
if err != nil {
    log.Fatal("monitor connect failed:", err)
}
defer ftpMonitor.Close()

// 分别认证与登录
config := tls.Config{InsecureSkipVerify: true}
ftpUpload.AuthTLS(config)
ftpUpload.Login("userName", "pass")

ftpMonitor.AuthTLS(config)
ftpMonitor.Login("userName", "pass")

// 切换工作目录(各自独立)
ftpUpload.Cwd("/home/myDir/")
ftpMonitor.Cwd("/home/myDir/")

file, err := os.Open("sth")
if err != nil {
    log.Fatal("open file failed:", err)
}
defer file.Close()

fmt.Println("start upload...")

// goroutine 1:上传文件
go func() {
    if err := ftpUpload.Stor("sth", file); err != nil {
        log.Printf("upload failed: %v", err)
    } else {
        fmt.Println("upload completed")
    }
}()

// goroutine 2:轮询监控(使用独立连接)
go func() {
    ticker := time.NewTicker(1 * time.Second)
    defer ticker.Stop()
    for range ticker.C {
        files, err := ftpMonitor.List("sth")
        if err != nil {
            log.Printf("list failed: %v", err)
            continue
        }
        for _, f := range files {
            fmt.Printf("File: %s, Size: %d, Modified: %s\n", f.Name, f.Size, f.Time)
        }
        if len(files) > 0 {
            fmt.Println("→ File detected. Upload likely in progress or complete.")
            break // 可选:检测到即退出
        }
    }
}()

// 防止主 goroutine 退出(实际项目中建议用 sync.WaitGroup 或 context 控制)
select {}

⚠️ 注意事项:

  • 绝不复用 FTP 实例:每个并发 FTP 操作(尤其是 Stor/Retr/List 等阻塞型命令)必须使用独立 *goftp.FTP 实例,否则必然触发底层 TCP 连接竞争;
  • 资源清理:务必对每个 ftp.Close() 显式调用(推荐 defer),避免连接泄漏;
  • 超时控制:生产环境应在 goftp.Connect() 前设置 net.Dialer.Timeout,并在 Stor() 调用中加入上下文超时(可通过封装或升级至支持 context 的 fork 版本);
  • 进度替代方案:goftp 原生不暴露上传字节流进度。如需精确进度(如百分比),建议改用 io.Pipe + 自定义 io.Reader 包装源文件,在读取时统计已传输字节数,再结合 ftp.Stor 使用(注意仍需单连接顺序执行);
  • 协议限制认知:FTP 本身无标准“上传进度推送”机制,List 轮询仅能反映服务端文件存在性/大小快照,无法替代真实流式进度反馈。

综上,解决 FTP 并发监控的核心在于连接隔离——用多个轻量级 FTP 连接分别承担上传与查询职责,既符合协议规范,又规避了竞态,是当前生态下最稳健的实践路径。

文章来自机圈观察员网,发布者:,转载请注明出处:https://www.jqgcy.com/xitongjiaocheng/123922.html

Bootstrap如何实现响应式的带图标卡片在移动端堆叠显示
上一篇 2026-07-01 14:39
HTML中Iframe在多Tab架构下的独立渲染进程分配与资源隔离实战
下一篇 2026-07-01 14:52

相关推荐