I am using vaadin upload to upload files on web application with polymer. And I am using golang for back-end.
<vaadin-upload target="../upload" max-files="20" accept="application/pdf,image/*"
method="POST"> </vaadin-upload>
I checked that encoding type used in vaadin upload is multipart/form-data. My golang code is below.
func upload(w http.ResponseWriter, r *http.Request) {
fmt.Println("method:", r.Method)
if r.Method == "GET" {
crutime := time.Now().Unix()
h := md5.New()
io.WriteString(h, strconv.FormatInt(crutime, 10))
token := fmt.Sprintf("%x", h.Sum(nil))
t, _ := template.ParseFiles("upload.gtpl")
t.Execute(w, token)
} else {
r.ParseMultipartForm(32 << 20)
file, handler, err := r.FormFile("uploadFile")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
fmt.Fprintf(w, "%v", handler.Header)
f, err := os.OpenFile("./test/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
io.Copy(f, file)
}
}
It gives error on server side http: no such file. I checked this error is returned by FormFile when the provided file field name is either not present in the request or not a file field.
How do I correct my form file name. Although everything seems fine on front-end
Answering my own question for those who are facing same problem. Found out some code which can access file without knowing the key value.
func UploadHandler(res http.ResponseWriter, req *http.Request) {
var (
status int
err error
)
defer func() {
if nil != err {
http.Error(res, err.Error(), status)
}
}()
// parse request
// const _24K = (1 << 20) * 24
if err = req.ParseMultipartForm(32 << 20); nil != err {
status = http.StatusInternalServerError
return
}
fmt.Println("No memory problem")
for _, fheaders := range req.MultipartForm.File {
for _, hdr := range fheaders {
// open uploaded
var infile multipart.File
if infile, err = hdr.Open(); nil != err {
status = http.StatusInternalServerError
return
}
// open destination
var outfile *os.File
if outfile, err = os.Create("./uploaded/" + hdr.Filename); nil != err {
status = http.StatusInternalServerError
return
}
// 32K buffer copy
var written int64
if written, err = io.Copy(outfile, infile); nil != err {
status = http.StatusInternalServerError
return
}
res.Write([]byte("uploaded file:" + hdr.Filename + ";length:" + strconv.Itoa(int(written))))
}
}
}
Did a quick test and it appears that the vaadin-upload
uses file
as the form data parameter name for the file. So in the line
file, handler, err := r.FormFile("uploadFile")
replace uploadFile
with file
and it should work.
It is documented on the vaadin homepage that
In order to support simultaneous uploads, instead of reusing the same FormData and XMLHttpRequest, we create a new one for each file. It is therefore alright for the server to only consider receiving one file per request.
However, I didn't see the parameter name (file
) documented, so to be safe you should write your code so that it wouldn't use the name, ie something like
r.ParseMultipartForm(32 << 20)
m := r.MultipartForm
for _, v := range m.File {
for _, f := range v {
file, err := f.Open()
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
// do something with the file data
...
}
}
Use exactly the same name for doing r.FormFile("selectedFile") that is there in your HTML tag. The key is mapped to input name used in your frontend HTML tag.
Below is my implementation:
<file
type="file"
name="selectedFile"
/>
const (
S3_REGION = "Your region"
S3_BUCKET = "Your Bucket Name"
)
func uploadFile(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Uploading File")
r.ParseMultipartForm(32 << 20)
file, handler, err := r.FormFile("selectedFile") //
if err != nil {
fmt.Println("Error Retrieving the File")
fmt.Println(err)
return
}
defer file.Close()
fmt.Printf("Uploaded File: %+v
", handler.Filename)
fmt.Printf("File Size: %+v
", handler.Size)
fmt.Printf("MIME Header: %+v
", handler.Header)
s, err := session.NewSession(&aws.Config{Region: aws.String(S3_REGION)})
if err != nil {
log.Fatal(err)
}
var size int64 = handler.Size
buffer := make([]byte, size)
file.Read(buffer)
_, err = s3.New(s).PutObject(&s3.PutObjectInput{
Bucket: aws.String(S3_BUCKET),
Key: aws.String(handler.Filename),
ACL: aws.String("private"),
Body: bytes.NewReader(buffer),
ContentLength: aws.Int64(size),
ContentType: aws.String(http.DetectContentType(buffer)),
ContentDisposition: aws.String("attachment"),
ServerSideEncryption: aws.String("AES256"),
})
}