Golang编写客户端、服务端并实现文件传输

栏目: Go · 发布时间: 6年前

内容简介:注意mux是一个实现了路由的对象。其带有ServeHttp方法。其实也可以是一般的HandlerFunc,只要是实现了ServeHTTP的对象都行。handler函数原型为:func HandlerDownload(w http.ResponseWriter, r *http.Request)

建立Server

server := &http.Server{
        Addr:    ":" + port,
        Handler: mux}

注意mux是一个实现了路由的对象。其带有ServeHttp方法。其实也可以是一般的HandlerFunc,只要是实现了ServeHTTP的对象都行。

handler函数原型为:

func HandlerDownload(w http.ResponseWriter, r *http.Request)

通过http.HandleFunc()函数,可将其改变为带有ServeHTTP方法的对象。

建立client request

client := &http.Client{}//建立客户端
    req, _ := http.NewRequest("GET", "http://baidu.com", nil)//建立request对象,第三个参数是body内容,建立之后可以设置head参数等。
    httputil.DumpRequest(req, false)//dump出远程服务器返回的信息
    resp, _ := client.Do(req)//客户端发出request请求
    body, _ := ioutil.ReadAll(resp.Body)
    defer resp.Body.Close()
    log.Println(string(body))

客户端与服务端信息传输,content-type主要为二进制或字符串。字符串可以自定义编码,二进制就是文件或图片等。不同的content-type含义:

x-www-form-urlencoded:客户端提交表格时候的编码方式,类似于url编码

form-data:也是表格,但是支持文件,不同key-value之间要用boundary分隔

binary:文件,只能是单个文件

json/html/plain text/xml等:都是字符串,交由浏览器解析

测试Handler

测试server的Handler函数,首先需要构建request对象,然后构建responseRecorder对象,记录handler的返回值。

func testHandler(t *testing.T) {
    param := url.Values{}
    param["page"] = []string{"1"}
    param["page_size"] = []string{"100"}
    //创建 request
    req, _ := http.NewRequest("GET", "/"+param.Encode(), nil)
    req.Header.Set("Authorization", "Token abc123")
    //创建 response
    rr := httptest.NewRecorder()
    //只测试函数,跳过mux路由
    HandlerTest(rr, req)

    log.Println(rr.Body.String())
    log.Println(rr.Result())
    log.Println(rr.Code)

}

文件上传

func testUpload(t *testing.T) {
    var client *http.Client
    var remoteURL string

        //构建server 对象
        ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            b, err := httputil.DumpRequest(r, true)
            if err != nil {
                panic(err)
            }
            fmt.Printf("%s", b)
        }))
        defer ts.Close()
        client = ts.Client()
        remoteURL = ts.URL

    //模拟客户端提交表格
    values := map[string]io.Reader{
        "file":   mustOpen("123.txt"), // 123.txt是本地文件
        "key":  strings.NewReader("hello world!"),
        "key2": strings.NewReader("hello world2!"),
    }
    err := Upload(client, remoteURL, values)
    if err != nil {
        panic(err)
    }
}
func Upload(client *http.Client, url string, values map[string]io.Reader) (err error) {
    // 构建multipart,然后post给服务端
    var b bytes.Buffer
    w := multipart.NewWriter(&b)
    for key, r := range values {
        var fw io.Writer
        if x, ok := r.(io.Closer); ok {
            defer x.Close()
        }
        // 添加文件
        if x, ok := r.(*os.File); ok {
            if fw, err = w.CreateFormFile(key, x.Name()); err != nil {
                return
            }
        } else {//添加字符串
            // Add other fields
            if fw, err = w.CreateFormField(key); err != nil {
                return
            }
        }
        if _, err = io.Copy(fw, r); err != nil {
            return err
        }

    }
    // close动作会在末端写入 boundary.
    w.Close()

    req, err := http.NewRequest("POST", url, &b)
    if err != nil {
        return
    }
    // form-data格式,且自动生成分隔符.
//例Content-Type: multipart/form-data; boundary=d76....d29
    req.Header.Set("Content-Type", w.FormDataContentType())

    // 提交 request
    res, err := client.Do(req)
    if err != nil {
        return
    }

    // 检查返回状态吗
    if res.StatusCode != http.StatusOK {
        err = fmt.Errorf("bad status: %s", res.Status)
    }
    return
}

func mustOpen(f string) *os.File {
    r, err := os.Open(f)
    if err != nil {
        panic(err)
    }
    return r
}

form-data的body样例:

//文件部分

--d6...9

Content-Disposition: form-data; name="file"; filename="123.txt"

Content-Type: application/octet-stream

//key value部分:w.CreateFormField(key);

--d6.....9

Content-Disposition: form-data; name="file"

//最末端还有分隔符

--d6.....9--

server端处理文件

保存客户端上传的文件

Request.FormFile("FormFile")得到file对象和file头

然后用os.File创建本地文件

最后用io.Copy将request文件保存到目标文件。

向客户端推送文件

os.File打开文件,

用file.Stat()获取文件属性

设置文件头

w.Header().Set("Content-Type", "application/x-zip-compressed")

//也可用http.DetectContentType获得文件类型:application/zip

w.Header().Set("Content-Disposition", "attachment; filename="+fileSta.Name())

w.Header().Set("Content-Length", strconv.FormatInt(fileSta.Size(), 10))

推送文件

io.Copy(ResponseWriter,file)


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Remote

Remote

Jason Fried、David Heinemeier Hansson / Crown Business / 2013-10-29 / CAD 26.95

The “work from home” phenomenon is thoroughly explored in this illuminating new book from bestselling 37signals founders Fried and Hansson, who point to the surging trend of employees working from hom......一起来看看 《Remote》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换