網頁

2022/6/15

Golang http POST multipart form file

Go http發送表單檔案範例。


範例環境:

  • Go 1.18


範例

下面範例有兩個API,一為GET|/send,一為POST|uploadGET|/send中會從專案目錄static/image取得gopher.jpg檔案,然後將檔案內容寫至http.Request.Body後透過http.Client.Do()發送請求給POST|uploadPOST|upload收到表單請求後將檔案取出存放在專案跟目錄。

注意multipart.NewWriter()取得的multipart.Writer要在檔案內容複製到multipart.Writer.CreateFormFile()建立的io.Writer就要呼叫multipart.Writer.Close()關閉。

若在請求建立後才關閉則發送請求時會發生transport connection broken: http: ContentLength=<length-a> with Body length <length-b>錯誤。而接收端取得檔案時會出現multipart: NextPart: EOF錯誤。

若在檔案複製到multipart.Writer.CreateFormFile()建立的io.Writer前關閉則會出現multipart: can't write to finished part錯誤。

main.go

package main

import (
    "bytes"
    "fmt"
    "io"
    "io/ioutil"
    "mime/multipart"
    "net/http"
    "os"
)

func main() {
    http.HandleFunc("/send", func(w http.ResponseWriter, r *http.Request) {
        switch r.Method {
        case http.MethodGet:

            body := new(bytes.Buffer) // the body content writed by multipart.Writer
            mw := multipart.NewWriter(body)

            // write field "name"
            fw, err := mw.CreateFormField("name")
            if err != nil {
                panic(err)
            }
            _, err = fw.Write([]byte("John"))
            if err != nil {
                panic(err)
            }

            // write field "age"
            fw, err = mw.CreateFormField("age")
            if err != nil {
                panic(err)
            }
            _, err = fw.Write([]byte("33"))
            if err != nil {
                panic(err)
            }

            // write file field "file"
            fw, err = mw.CreateFormFile("file", "gopher.jpg")
            if err != nil {
                panic(err)
            }

            f, err := os.Open("./static/image/gopher.jpg")
            if err != nil {
                panic(err)
            }
            defer f.Close()

            _, err = io.Copy(fw, f)
            if err != nil {
                panic(err)
            }
            mw.Close() // close multipart.Writer

            req, err := http.NewRequest(http.MethodPost, "http://localhost:8080/upload", body)
            if err != nil {
                panic(err)
            }
            req.Header.Set("Content-Type", mw.FormDataContentType())

            client := &http.Client{}
            _, err = client.Do(req)
            if err != nil {
                panic(err)
            }

            fmt.Fprint(w, "success")
        default:
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        }
    })

    http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
        switch r.Method {
        case http.MethodPost:
            r.ParseMultipartForm(1 << 20) // 1MB

            name := r.Form.Get("name") // get value from field "name"
            age := r.Form.Get("age") // get value from field "age"

            file, header, err := r.FormFile("file") // get file of field "file"
            if err != nil {
                panic(err)
            }
            defer file.Close()

            buf := bytes.NewBuffer(nil)
            _, err = io.Copy(buf, file)
            if err != nil {
                panic(err)
            }
            filename := header.Filename
            err = ioutil.WriteFile(filename, buf.Bytes(), 0666)
            if err != nil {
                panic(err)
            }

            fmt.Printf("name=%v\nage=%v\nfilename=%v\n", name, age, filename)
            
            w.Header().Set("Content-Type", "application/json")
            fmt.Fprintf(w, "%v uploaded", filename)
        default:
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        }
    })

    http.ListenAndServe(":8080", nil)
}



測試

使用curl輸入curl -X GET "http://localhost:8080/send"。若執行成功在專案跟目錄會新增一個gopher.jpg

$ curl -X GET "http://localhost:8080/send"
success

在console會印出以下:

name=John
age=33
filename=gopher.jpg



沒有留言:

張貼留言