开启掘金成长之旅!这是我参与「掘金日新计划 · 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)