Go网络文件传输

流程分析

借助TCP完成文件的传输,基本思路如下:

  1. 发送方(客户端)向服务端发送文件名,服务端保存该文件名。
  2. 接收方(服务端)向客户端返回一个消息ok,确认文件名保存成功。
  3. 发送方(客户端)收到消息后,开始向服务端发送文件数据。
  4. 接收方(服务端)读取文件内容,写入到之前保存好的文件中。

由于文件传输需要稳定可靠的连接,所以采用TCP方式完成网络文件传输功能。

首先获取文件名。借助os包中的stat()函数来获取文件属性信息。在函数返回的文件属性中包含文件名和文件大小。Stat参数name传入的是文件访问的绝对路径。FileInfo中的Name()函数可以将文件名单独提取出来。

func Stat(name string) (fi FileInfo, err error)

Stat返回一个描述name指定的文件对象的FileInfo。如果指定的文件对象是一个符号链接,返回的FileInfo描述该符号链接指向的文件的信息,本函数会尝试跳转该链接。如果出错,返回的错误值为*PathError类型。

我们通过源码可以得知FileInfo是一个接口,要实现这个接口就必须实现这个接口的如下所有方法

实现网络文件传输实质上时借助了本地文件复制和TCP网络编程相关知识,可以先看看Go语言复制文件Go网络编程了解相关内容。

所以关于使用TCP实现文件传输大致步骤可以归结为如下步骤

接收端:

  1. 创建监听 listener,程序结束时关闭。
  2. 阻塞等待客户端连接 conn,程序结束时关闭conn。
  3. 读取客户端发送文件名。保存 fileName。
  4. 回发“ok”。
  5. 封装函数 RecvFile 接收客户端发送的文件内容。传参 fileName 和 conn
  6. 按文件名 Create 文件,结束时 Close
  7. 循环 Read 发送端网络文件内容,当读到 0 说明文件读取完毕。
  8. 将读到的内容原封不动Write到创建的文件中

接收端代码:

package main

import (
    "fmt"
    "io"
    "net"
    "os"
)

func recvFile(conn net.Conn, fileName string) {
    //按照文件名创建新文件
    file, err := os.Create(fileName)
    if err != nil {
        fmt.Printf("os.Create()函数执行错误,错误为:%v\n", err)
        return
    }
    defer file.Close()

    //从网络中读数据,写入本地文件
    for {
        buf := make([]byte, 4096)
        n, err := conn.Read(buf)

        //写入本地文件,读多少,写多少
        file.Write(buf[:n])
        if err != nil {
            if err == io.EOF {
                fmt.Printf("接收文件完成。\n")
            } else {
                fmt.Printf("conn.Read()方法执行出错,错误为:%v\n", err)
            }
            return
        }
    }
}

func main() {

    //1.创建监听socket
    listener, err := net.Listen("tcp", "127.0.0.1:8000")
    if err != nil {
        fmt.Printf("net.Listen()函数执行错误,错误为:%v\n", err)
        return
    }
    defer listener.Close()

    //阻塞监听
    conn, err := listener.Accept()
    if err != nil {
        fmt.Printf("listener.Accept()方法执行错误,错误为:%v\n", err)
        return
    }
    defer conn.Close()

    //文件名的长度不能超过1024个字节
    buf := make([]byte, 4096)
    n, err := conn.Read(buf)
    if err != nil {
        fmt.Printf("conn.Read()方法执行错误,错误为:%v\n", err)
        return
    }
    fileName := string(buf[:n])

    //回写ok给发送端
    conn.Write([]byte("ok"))

    //获取文件内容
    recvFile(conn, fileName)
}

发送端:

  1. 提示用户使用命令行参数输入文件名。接收文件名 filepath(含访问路径)
  2. 使用 os.Stat()获取文件属性,得到纯文件名 fileName(去除访问路径)
  3. 主动发起连接服务器请求,结束时关闭连接。
  4. 发送文件名到接收端 conn.Write()
  5. 读取接收端回发的确认数据 conn.Read()
  6. 判断是否为“ok”。如果是,封装函数 SendFile() 发送文件内容。传参 filePath 和 conn
  7. 只读 Open 文件, 结束时Close文件
  8. 循环读本地文件,读到 EOF,读取完毕。
  9. 将读到的内容原封不动 conn.Write 给接收端(服务器)

发送端代码:

package main

import (
    "fmt"
    "io"
    "net"
    "os"
)
func sendFile(conn net.Conn, filePath string) {
    //只读打开文件
    file, err := os.Open(filePath)
    if err != nil {
        fmt.Printf("os.Open()函数执行出错,错误为:%v\n", err)
        return
    }
    defer file.Close()

    buf := make([]byte, 4096)
    for {
        //从本地文件中读数据,写给网络接收端。读多少,写多少
        n, err := file.Read(buf)
        if err != nil {
            if err == io.EOF {
                fmt.Printf("发送文件完毕\n")
            } else {
                fmt.Printf("file.Read()方法执行错误,错误为:%v\n", err)
            }
            return
        }
        //写到网络socket中
        _, err = conn.Write(buf[:n])
    }
}

func main() {

    //获取命令行参数
    list := os.Args

    if len(list) != 2 {
        fmt.Printf("格式为:go run xxx.go 文件名\n")
        return
    }

    //提取文件的绝对路径
    path := list[1]

    //获取文件属性
    fileInfo, err := os.Stat(path)
    if err != nil {
        fmt.Printf("os.Stat()函数执行出错,错误为:%v\n", err)
        return
    }

    //主动发起连接请求
    conn, err := net.Dial("tcp", "127.0.0.1:8000")
    if err != nil {
        fmt.Printf("net.Dial()函数执行出错,错误为:%v\n", err)
        return
    }
    defer conn.Close()

    //发送文件名给接收端
    _, err = conn.Write([]byte(fileInfo.Name()))

    //读取服务器回发数据
    buf := make([]byte, 4096)
    n, err := conn.Read(buf)
    if err != nil {
        fmt.Printf("conn.Read(buf)方法执行出错,错误为:%v\n", err)
        return
    }

    if string(buf[:n]) == "ok" {
        //写文件内容给服务器 -- 借助conn
        sendFile(conn, path)
    }
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 使用Go语言实现文件传输可以使用net/http包的HandleFile函数。它可以接受一个文件路径,然后将文件内容作为响应发送给客户端。下面是一个简单的示例:http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.ServeFile(w, r, "./myfile.txt") }) ### 回答2: 使用Go语言实现文件传输可以通过以下步骤进行: 1. 导入所需的包:在Go语言中,使用`os`包操作文件系统,使用`net`包进行网络通信。 2. 创建TCP服务器:使用`net.Listen`函数创建一个TCP服务器,并指定监听的地址和端口。 3. 监听客户端连接:使用`Accept`方法在服务器上监听客户端的连接请求,并创建一个新的TCP连接。 4. 接收文件数据:使用`io.Copy`函数从客户端连接中读取文件数据,并保存到本地的文件中。 5. 关闭连接:在文件传输完成后,关闭连接。 以下是一个简单的示例代码: ```go package main import ( "fmt" "io" "net" "os" ) func main() { // 创建TCP服务器 listener, err := net.Listen("tcp", "localhost:8000") if err != nil { fmt.Println("Error listening:", err.Error()) os.Exit(1) } defer listener.Close() fmt.Println("Server started. Listening on localhost:8000") // 监听客户端连接 conn, err := listener.Accept() if err != nil { fmt.Println("Error accepting connection:", err.Error()) os.Exit(1) } defer conn.Close() fmt.Println("Client connected.") // 创建文件来保存接收到的数据 outputFile, err := os.Create("output.txt") if err != nil { fmt.Println("Error creating file:", err.Error()) os.Exit(1) } defer outputFile.Close() // 接收文件数据并保存到指定文件中 _, err = io.Copy(outputFile, conn) if err != nil { fmt.Println("Error receiving file:", err.Error()) os.Exit(1) } fmt.Println("File transfer complete.") } ``` 以上代码演示了一个简单的文件传输服务器端,它会监听在本地的8000端口上,等待客户端连接。一旦有客户端连接,它会接收该客户端传输文件,并将文件保存在本地的`output.txt`文件中。在实际生产环境中,你可能需要添加更多的错误处理和安全性措施。 ### 回答3: 使用Go语言实现文件传输可以通过以下步骤完成: 1. 导入所需的包:使用Go语言实现文件传输需要使用`net`和`os`包,分别用于处理网络连接和操作系统相关的文件操作。 2. 创建服务器端:使用`net`包中的`Listen`函数创建一个`net.Listener`对象,指定服务器要监听的端口和IP地址。用`Accept`方法接受客户端的连接,并使用`io.Copy`方法将收到的文件写入到服务器上的目标文件。 3. 创建客户端:使用`net`包中的`Dial`方法连接到服务器的IP地址和端口号。打开本地文件,并使用`io.Copy`方法将文件内容发送给服务器。 4. 异常处理:在编写代码时,应对可能出现的错误进行适当的异常处理,例如检查文件是否存在、网络连接是否正常以及读写文件时是否发生错误。 5. 测试代码:编写测试代码以验证文件传输功能是否正常工作。可以模拟服务器和客户端之间的文件传输,检查传输文件是否一致。 总结:使用Go语言实现文件传输需要使用`net`和`os`包,通过创建服务器端和客户端的代码,实现文件的发送和接收。在编写代码时要注意异常处理,并进行测试以验证文件传输功能的正确性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值