Ivan R
Ivan R

Reputation: 1923

golang POST image and text field with multipart/form-data

Yes, I have found answers:

http://matt.aimonetti.net/posts/2013/07/01/golang-multipart-file-upload-example/

golang POST data using the Content-Type multipart/form-data

HTTP-POST file multipart programming in Go language

And answers not helpful, because I'm get error with multipart.NewWriter. My version of go lang 1.3.3

I'm trying to send some text data and image with form(templates/form.html)

    <form enctype="multipart/form-data" method="POST">
      <div class="row">
        <div class="large-6 columns">
          <label>Image
            <input type="file" name="Image" class="button"/>
          </label>
        </div>

     </div>
    <div class="row">
        <div class="large-12 columns">
          <label>About Me
            <textarea type="text" name="aboutMySelf" class="aboutMySelf"></textarea>
          </label>
        </div>
    </div>
</form>

And my Go approach is like this:

package main

import (

    "fmt"
    "html/template"
    "io"
    "log"
    "net/http"
    "net/textproto"
    "os"
    "reflect"
    "time"

    "github.com/gorilla/mux"
    "github.com/gorilla/schema"

)



func render(w http.ResponseWriter, tmpl string, context map[string]interface{}) {
    tmpl_list := []string{fmt.Sprintf("templates/%s.html", tmpl)}
    t, err := template.ParseFiles(tmpl_list...)
    if err != nil {
        log.Print("template parsing error: ", err)
    }
    err = t.Execute(w, context)
    if err != nil {
        log.Print("template executing error: ", err)
    }
}
    type FileHeader struct {
        Filename string
        Header   textproto.MIMEHeader
        // contains filtered or unexported fields
    }
     func uploadImage(q *http.Request, nameInForm string) {
        q.ParseMultipartForm(32 << 20)

        file, _, err := q.FormFile(nameInForm)

        if err != nil {
            fmt.Println(err)

        }
        defer file.Close()

        f, err := os.OpenFile("./static/uploadimages/"+handler.Filename+".jpg", os.O_WRONLY|os.O_CREATE, 0666)
        if err != nil {
            fmt.Println(err)

        }
        defer f.Close()
        io.Copy(f, file)
    }
func main() {

    rtr := mux.NewRouter()



    //ADMIN SECTION
    rtr.HandleFunc("/myself", myself).Methods("GET")
    rtr.HandleFunc("/myself", myselfP).Methods("POST")


    rtr.PathPrefix("/").Handler(http.FileServer(http.Dir("./static/")))

    http.Handle("/", rtr)

    log.Println("Listening...")
    http.ListenAndServe(":3000", nil)
}
func myself(w http.ResponseWriter, q *http.Request) {

    render(w,"form",nil)

}
func myselfP(w http.ResponseWriter, q *http.Request) {
    err := q.ParseForm()

    if err != nil {
        // Handle error
        fmt.Println(err)
    }

    uploadImage(q, "Image")


    http.Redirect(w, q, "/myself", http.StatusFound)

}

File uploads perfectly, but I cann't get data from textarea. I'm trying understand what different between simple form with fields and multipart form, and how I can get data from fields

Thanks and regards

Upvotes: 4

Views: 11653

Answers (1)

Alexander Jeyaraj
Alexander Jeyaraj

Reputation: 833

You can get a multipart.Part from multipart.Reader. The Part.FileName() will be empty if the parameter is not a File. You can get the reader by calling r.MultipartReader(). Once you get the reader the following code will get you array of Files and map of other fields.

func ReadFormData(mr *multipart.Reader) ([]FileParam, map[string]interface{}, error) {
    var part *multipart.Part
    var err error
    paramFiles := []FileParam{}
    paramTexts := map[string]interface{}{}
    for {
        if part, err = mr.NextPart(); err != nil {
            if err != io.EOF { //io.EOF error means reading is complete
                return paramFiles, paramTexts, fmt.Errorf(" error reading multipart request: %+v", err)
            }
            break
        }

        if part.FileName() != "" {
            content, err := readFileParam(part)
            if err != nil {
                return paramFiles, paramTexts, fmt.Errorf(" error reading file param %s: %+v", part.FileName, err)
            }

            fileItem := FileParam{
                Key:      "configFiles",
                FileName: part.FileName(),
                Content:  content,
            }
            paramFiles = append(paramFiles, fileItem)
        } else {
            name := part.FormName()
            value := readValueFromFormData(part)
            paramTexts[name] = value
        }

    }
    return paramFiles, paramTexts, nil
}

func readValueFromFormData(part *multipart.Part) string {
    buf := new(bytes.Buffer)
    buf.ReadFrom(part)
    return buf.String()
}

func readFileParam(part *multipart.Part) ([]byte, error) {
    content := []byte{}
    uploaded := false
    var err error

    for !uploaded {
        chunk := make([]byte, 4096)
        if _, err = part.Read(chunk); err != nil {
            if err != io.EOF {
                return nil, fmt.Errorf(" error reading multipart file %+v", err)
            }
            uploaded = true
        }
        content = append(content, chunk...)
    }

    return content, nil
}

type FileParam struct {
    Key      string
    FileName string
    Content  []byte
}

Upvotes: 0

Related Questions