无法使用GOLANG和POLYMER http将上传的文件保存在服务器上:无此文件

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 enter image description here

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"),
    })       
}