golang 使用mime/multipart传输附件

CodingCode · · 1291 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

golang 使用mime/multipart传输附件

client部分

package main

import (
    "io"
    "os"
    "fmt"
    "log"
    "bytes"
    "path/filepath"
    "net/http"
    "mime/multipart"

    "crypto/md5"
    "encoding/hex"
)

func main() {
    bodyBuffer := &bytes.Buffer{}
    bodyWriter := multipart.NewWriter(bodyBuffer)

    if err := attachField(bodyWriter, "key1", "value1"); err != nil {
        return
    }
    
    if err := attachField(bodyWriter, "key2", "value2"); err != nil {
        return
    }

    if err := attachFile(bodyWriter, "fileform1", "a1.txt"); err != nil {
        return
    }

    if err := attachFile(bodyWriter, "fileform1", "a2.txt"); err != nil {
        return
    }
    if err := attachField(bodyWriter, "key3", "value3"); err != nil {
        return
    }
    if err := attachFile(bodyWriter, "fileform2", "a2.txt"); err != nil {
        return
    }
    if err := attachFile(bodyWriter, "fileform1", "a3.txt"); err != nil {
        return
    }
    contentType := bodyWriter.FormDataContentType()
    bodyWriter.Close()

    uri := fmt.Sprintf("http://localhost:%d/multipart", 8082)
    req, err := http.NewRequest("POST", uri, bodyBuffer)
    if err != nil {
        log.Printf("Cannot NewRequest: %s , err: %v", uri, err)
        return
    }

    //req.SetBasicAuth(c.user, c.passwd)
    req.Header.Set("Content-Type", contentType)


    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        log.Printf("Cannot client.Do, err: %v", err)
        return
    }
    defer resp.Body.Close()
    //dumpResponse(resp)
}

func attachField(bodyWriter * multipart.Writer, keyname, keyvalue string) error {
    if err := bodyWriter.WriteField(keyname, keyvalue); err != nil {
        log.Printf("Cannot WriteField: %s, err: %v", keyname, err)
        return err
    }
    return nil
}

func attachFile(bodyWriter * multipart.Writer, formname, filename string) error {
    fullname := filepath.Join(".", filename)
    file, err := os.Open(fullname)
    if err != nil {
        log.Printf("Cannot open file: %s , err: %v", fullname, err)
        return err
    }
    defer file.Close()

    // MD5
    md5hash := md5.New()
    if _, err = io.Copy(md5hash, file); err != nil {
        log.Printf("Cannot open md5 hash: %s , err: %v", fullname, err)
        return err
    }

    keyname  := filename + ".md5cksum"
    keyvalue := hex.EncodeToString(md5hash.Sum(nil)[:16])
    if err = attachField(bodyWriter, keyname, keyvalue); err != nil {
        log.Printf("Cannot WriteField: %s, err: %v", keyname, err)
        return err
    }

    // file
    part, err := bodyWriter.CreateFormFile(formname, filename)
    if err != nil {
        log.Printf("Cannot CreateFormFile for: %s , err: %v", filename, err)
        return err
    }

    _, err = io.Copy(part, file)
    if err != nil {
        log.Printf("Cannot Copy file: %s , err: %v", fullname, err)
        return err
    }

    return nil
}

server部分

package main

import (
    "io"
    "os"
    "fmt"
    "log"
    "net/http"
    "io/ioutil"
    "encoding/json"

    "github.com/gorilla/mux"
)

func main() {
    address := fmt.Sprintf("%s:%d", "0.0.0.0", 8082)
    router  := mux.NewRouter().StrictSlash(true)

    router.HandleFunc("/multipart", testHandle)
    err := http.ListenAndServe(address, router)
    if err != nil {
        log.Panic("ListenAndServe err:", err)
    }
}


func writeErrorResponse(code int, err error, w http.ResponseWriter) {
    response := map[string]string {"error": err.Error()}
    writeResponse(code, response, w)
}

func writeResponse(code int, jsonres interface{}, w http.ResponseWriter) {
    b, err := json.Marshal(jsonres)
    if err != nil {
        w.WriteHeader(http.StatusInternalServerError)
        w.Write([]byte(err.Error()))
    } else {
        w.Header().Set("Content-Type", "application/json")
        w.WriteHeader(code)
        w.Write(b)
    }
}

func testHandle(w http.ResponseWriter, r *http.Request) {
    log.Printf("Entry of handle\n");
    reader, err := r.MultipartReader()
    if err != nil {
        writeErrorResponse(http.StatusBadRequest, err, w)
        return
    }

    for {
        part, err := reader.NextPart()
        if err == io.EOF {
            log.Printf("==== EOF\n");
            break
        } else if err != nil {
            log.Printf("==== ERROR\n");
            writeErrorResponse(http.StatusBadRequest, err, w)
            return
        }

        filename := part.FileName()
        formname := part.FormName()
        log.Printf("==== filename=[%s], forname=[%s]\n", filename, formname);
        if filename != "" {
            dst, err := os.Create(filename + ".new")
            if err != nil {
                writeErrorResponse(http.StatusBadRequest, err, w)
                return
            }
            defer dst.Close()
            _, err = io.Copy(dst, part)
            if err != nil {
                dst.Close()
                writeErrorResponse(http.StatusBadRequest, err, w)
                return
            }
        } else if formname == "key1" {
            data, err := ioutil.ReadAll(part)
            if err != nil {
                writeErrorResponse(http.StatusBadRequest, err, w)
                return
            }
            log.Printf("forname[%s]=[%s]\n", formname, string(data))
        }
    }

    writeResponse(http.StatusAccepted, nil, w)
}

运行结果server端输出:

2019/09/05 05:14:16 Entry of handle
2019/09/05 05:14:16 ==== filename=[], forname=[key1]
2019/09/05 05:14:16 ==== filename=[], forname=[key2]
2019/09/05 05:14:16 ==== filename=[], forname=[a1.txt.md5cksum]
2019/09/05 05:14:16 ==== filename=[a1.txt], forname=[fileform1]
2019/09/05 05:14:16 ==== filename=[], forname=[a2.txt.md5cksum]
2019/09/05 05:14:16 ==== filename=[a2.txt], forname=[fileform1]
2019/09/05 05:14:16 ==== filename=[], forname=[key3]
2019/09/05 05:14:16 ==== filename=[], forname=[a2.txt.md5cksum]
2019/09/05 05:14:16 ==== filename=[a2.txt], forname=[fileform2]
2019/09/05 05:14:16 ==== filename=[], forname=[a3.txt.md5cksum]
2019/09/05 05:14:16 ==== filename=[a3.txt], forname=[fileform1]
2019/09/05 05:14:16 ==== EOF

我们可以观察到:

  1. 文件form包含文件名,而普通field form没有文件名
  2. 所有form的排序和加入时是一致的,就是按照加入的先后顺序读出,不区分是字段form还是文件form,也不按照字段的名字排序。

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:CodingCode

查看原文:golang 使用mime/multipart传输附件

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1291 次点击  ∙  1 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传