在 Golang 中访问已上传的文件

10

我在使用golang上传文件时遇到了问题。我对这门语言非常陌生,尝试了多次但是没有在网上找到任何答案。

我做错了什么?在这段代码中,我从未到达列出上传的文件数量的代码块。

func handler(w http.ResponseWriter, r *http.Request) {
  fmt.Println("handling req...")

  if r.Method =="GET"{
    fmt.Println("GET req...")

  } else {

    //parse the multipart stuff if there
    err := r.ParseMultipartForm(15485760)

    //
    if err == nil{
        form:=r.MultipartForm
        if form==nil {
            fmt.Println("no files...")

        } else {
            defer form.RemoveAll()
            // i never see this actually occur
            fmt.Printf("%d files",len(form.File))
        }
    } else {
        http.Error(w,err.Error(),http.StatusInternalServerError)
        fmt.Println(err.Error())
    }
  }

  //fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
  fmt.Println("leaving...")
}

更新

我成功让上述代码运行了,这很棒。下面的答案展示了如何使用异步方式,可能是一个比我的代码样例更好的选择。


你看到了什么?如果它从未进入具有len(form.File)的代码块,那么它会采取哪条其他的执行路径? - Volker
@Volker 我看到它一直在说没有文件 - form==nil 的路径。 - Micah
1
虚拟代码(只是您在“/”和主函数上的处理程序)表明您的代码正确识别文件(使用“curl -F file = @ somefile http:// localhost:8080”发布)。如果您尝试从页面上传文件,请在表单中设置正确的内容类型。 - mechmind
3
请注意,如果您使用流式阅读器,就不能依赖于字段顺序。 - mechmind
1
@Jonno 我已经准备好接受了。 - Micah
显示剩余5条评论
2个回答

12

回答 下载最新的golang版本。

我之前遇到过这个问题,使用旧版本的golang时发生了什么我不知道,但是使用最新版本的golang可以正常工作。 =)

我下面是上传处理程序代码... 完整代码请看:http://noypi-linux.blogspot.com/2014/07/golang-web-server-basic-operatons-using.html

  // parse request  
  const _24K = (1 << 10) * 24  
  if err = req.ParseMultipartForm(_24K); nil != err {  
       status = http.StatusInternalServerError  
       return  
  }  
  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))))  
       }  
  }  

2
const _24K = (1 << 20) * 24<-- 这不是24MB吗?2^20 = 1MB - Gonzalo Serrano
1
在复制文件后,我应该删除临时目录中的文件吗?我注意到一些大小大于_24K(或其他大小)的文件出现在我的'/tmp'目录中,但我真的不知道如何删除它们。os.Remove(hdr.Filename)没有任何作用,因为找不到临时文件(因为其名称与上传的文件不同)。 - 18augst

5
如果你知道文件上传的密钥,我认为可以使它变得简单一些(这未经过测试):
infile, header, err := r.FormFile("file")
if err != nil {
    http.Error(w, "Error parsing uploaded file: "+err.Error(), http.StatusBadRequest)
    return
}

// THIS IS VERY INSECURE! DO NOT DO THIS!
outfile, err := os.Create("./uploaded/" + header.Filename)
if err != nil {
    http.Error(w, "Error saving file: "+err.Error(), http.StatusBadRequest)
    return
}

_, err = io.Copy(outfile, infile)
if err != nil {
    http.Error(w, "Error saving file: "+err.Error(), http.StatusBadRequest)
    return
}

fmt.Fprintln(w, "Ok")

1
你创建文件的方式有哪些不安全的地方?有没有更好的方法? - THUNDERGROOVE
2
文件名可能是 ../../../../../etc/passwd 或其他任何东西。基本上我没有对文件名进行过滤或检查是否已经存在其他文件。虽然 Go 可能会从文件名中删除 \ 和 /,但我不会依赖它。 - Timmmm
那正是我想的。谢谢!我想最好的做法是不允许 / 或 \ 。我的想法正确吗?还有什么需要担心的吗? - THUNDERGROOVE
应该可以了(至少在非Windows平台上)。我会将我知道是安全的字符列入白名单,并对其余部分进行URI编码。 - Timmmm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接