4

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

Community
  • 1
  • 1
Ivan R
  • 1,883
  • 4
  • 15
  • 15
  • Maybe you could condens your code to what actually doesn't work. I understand that file uploads works, but textarea input not. But your Go code never tries to read the textarea data? What's wrong with Request.Form or Request.FormValue? – Volker Nov 27 '14 at 11:17
  • @ Volker q.Formvalue("aboutMySelf") output empty string. I think in multipart form I need something else, if I want to get data from textarea – Ivan R Nov 27 '14 at 11:23
  • 3
    Make sure you do q.Formvalue("aboutMySelf") **after** calling ParseMultipartForm. – Volker Nov 27 '14 at 11:59
  • @Volker Yes! ParseMultipartForm(). Thanks! – Ivan R Nov 27 '14 at 17:10

1 Answers1

-1

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
}