【Go网络编程】tcp传输文件

306 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 13 天,点击查看活动详情

文件发送

文件发送是tcp常用的功能之一,上次我们介绍了tcp和socket编程。

【Go网络编程】tcp与udp - 掘金 (juejin.cn) 【Go网络编程】socket编程 - 掘金 (juejin.cn)

下面继续对文件传输部分进行实现。文件传输也是文件以二进制的方式传输,但是涉及文件的读取、发送、接收、文件保存等,读取和保存牵扯到文件路径和文件名,因此在发送时需要把文件名也发送给对方。

文件发送与接收

服务端(接收):

func server(path string) {
   // 监听端口
   listener, err := net.Listen("tcp", ":1234")
   if err != nil {
      fmt.Println("监听失败:", err)
      return
   }
   defer listener.Close()

   fmt.Println("等待连接...")

   // 接收连接并处理
   for {
      conn, err := listener.Accept()
      if err != nil {
         fmt.Println("连接失败:", err)
         continue
      }
      fmt.Println("连接成功")

      // 获取上传的文件名
      filename := make([]byte, 1024)
      n, err := conn.Read(filename)
      if err != nil {
         fmt.Println(err)
         return
      }
      fmt.Println("接收文件名:", string(filename[:n]))
      // 创建本地文件并接收上传的文件内容
      dst, err := os.Create(filepath.Join(path, string(filename[:n])))
      if err != nil {
         fmt.Println(err)
         return
      }

      _, err = io.Copy(dst, conn)
      if err != nil {
         fmt.Println("接收文件失败:", err)
         continue
      }

      dst.Close()
      fmt.Println("File saved successfully:", dst)
   }
}

客户端(发送):

func client(path string) {
   conn, err := net.Dial("tcp", "localhost:1234")
   if err != nil {
      fmt.Println(err)
   }
   defer conn.Close()

   info, err := os.Stat(path)
   if err == nil && !info.IsDir() {
      s := strings.Split(path, "/")
      if _, err := conn.Write([]byte(s[len(s)-1])); err != nil {
         fmt.Println(err)
      }
      file, err := os.Open(path)
      if err != nil {
         fmt.Println(err)
      }
      defer file.Close()
      // 读取文件内容并写入连接
      _, err = io.Copy(conn, file)
      if err != nil {
         fmt.Println(err)
      }
      fmt.Println("All files uploaded successfully")
   }else {
        fmt.Println("文件路径错误")
    }
}

发送时显示进度

为了动态展示发送时的进度,可以构造一个Process类,传输时同时现实动态进度

// 进度条结构体
type ProgressBar struct {
   total    int64
   current  int64
   previous int64
}

// 创建新的进度条
func NewProgressBar(total int64) *ProgressBar {
   return &ProgressBar{
      total: total,
   }
}

// 实现 io.Writer 接口
func (p *ProgressBar) Write(b []byte) (n int, err error) {
   n = len(b)
   p.current += int64(n)

   // 计算进度
   progress := int(100 * p.current / p.total)

   // 如果进度有变化,则打印进度
   if progress != int(100*p.previous/p.total) {
      fmt.Printf("\rProgress: %d%%", progress)
      p.previous = p.current
   }

   return
}

// 获取文件大小
fileInfo, _ := file.Stat()
fileSize := fileInfo.Size()
// 创建进度条
bar := NewProgressBar(fileSize)
// 使用 io.Copy() 进行传输
_, err = io.Copy(io.MultiWriter(conn, bar), file)