Go網絡文件傳輸

流程分析

藉助TCP完成文件的傳輸,基本思路以下:html

  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是一個接口,要實現這個接口就必須實現這個接口的以下全部方法socket

實現網絡文件傳輸實質上時藉助了本地文件複製和TCP網絡編程相關知識,能夠先看看Go語言複製文件Go網絡編程瞭解相關內容。tcp

因此關於使用TCP實現文件傳輸大體步驟能夠歸結爲以下步驟函數

接收端:命令行

  1. 建立監聽 listener,程序結束時關閉。
  2. 阻塞等待客戶端鏈接 conn,程序結束時關閉conn。
  3. 讀取客戶端發送文件名。保存 fileName。
  4. 回發「ok」。
  5. 封裝函數 RecvFile 接收客戶端發送的文件內容。傳參 fileName 和 conn
  6. 按文件名 Create 文件,結束時 Close
  7. 循環 Read 發送端網絡文件內容,當讀到 0 說明文件讀取完畢。
  8. 將讀到的內容原封不動Write到建立的文件中

接收端代碼:3d

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)
}

發送端:code

  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)
    }
}
相關文章
相關標籤/搜索