Reputation: 1923
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
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