用go处理文件上传 [英] Handle file uploading with go

查看:1003
本文介绍了用go处理文件上传的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近刚开始玩,所以我仍然是一个小菜鸟,抱歉,如果我犯了太多错误。我一直试图解决这个问题很长一段时间,但我只是不明白发生了什么。在我的main.go文件中,我有一个主要函数:

$ p $ func main(){
http.HandleFunc( /,处理程序)
http.HandleFunc(/ submit /,submit)
log.Fatal(http.ListenAndServe(:8080,nil))
}

处理函数如下所示:

  func处理程序(w http.ResponseWriter,r * http.Request){
data,_:= ioutil.ReadFile(web / index.html)
w.Write数据)
}

我知道这不是提供网站$ b的最佳方式$ b submit函数如下所示:

  func submit(w http.ResponseWriter,r * http.Request){
log.Println(METHOD IS+ r.Method +AND CONTENT-TYPE IS+ r.Header.Get(Content-Type))
r.ParseMultipartForm(32 <20 )
file,header,err:= r.FormFile(uploadFile)
if err!= nil {
json.NewEncoder(w).Encode(Response {err.Error() ,true})
返回
}
defer file.Close()
$ b $ out,err:= os.Create(/ tmp / file_+ time.Now()。String()+.png)
if err!= nil {
json.NewEncoder(w).Encode(Response {err.Error(),true})
return
}
推迟.Close()

_,err = io.Copy(out,file)
if err!= nil {
json.NewEncoder(w).Encode(Response {err .Error(),true})
return
}

json.NewEncoder(w).Encode(Response {File'+ header.Filename +'''submited successfully ,false})
}

问题是执行submit函数时, r.Method GET r.Header.Get(Content-Type)是一个空字符串,那么它会一直持续到第一个,如果r.FormFile返回以下错误:
请求Content-Type不是multipart / form-data
我不明白为什么r.Method总是GET并且没有Content-Type。我试图以许多不同的方式来执行index.html,但r.Method总是GET并且Content-Type是空的。这是index.html中上传文件的函数:

  function upload(){
var formData = new FormData ();
formData.append('uploadFile',document.querySelector('#file-input')。files [0]);
fetch('/ submit',{
method:'post',
headers:{
Content-Type:multipart / form-data
},
body:formData
})。then(function json(response){
return response.json()
})。then(function(data){
window.console.log('JSON响应请求成功',data);
})。catch(function(error){
window.console.log('Request failed',error) ;
});
}

以下是HTML:

 < input id =file-inputtype =filename =uploadFile/> 

请注意,标签不在标签内,我认为这可能是问题所以我改变了两个函数和HTML如下所示:

  function upload(){
fetch('/ submit', {
method:'post',
headers:{
Content-Type:multipart / form-data
},
body:new FormData document.querySelector('#form')
})。then(function json(response){
return response.json()
})。then(function(data){
window.console.log('JSON响应请求成功',data);
})。catch(function(error){
window.console.log('Request failed',error) ;
});
}

< form id =formmethod =postenctype =multipart / form-dataaction =/ submit> ;< input id =file-inputtype =filename =uploadFile/>< / form>

但是,这也没有奏效。我已经用Google搜索了如何使用fetch()以及如何从go上接收文件上传,我已经看到它们与我的很相似,我不知道我在做什么错误。



更新:
使用 curl -v -F'uploadFile = @ \C:/ Users / raul - / Desktop / test.png \'http:// localhost:8080 / submit 我得到以下输出:

  *尝试:: 1 ... 
*连接到本地主机(:: 1)端口8080(#0)
> POST /提交HTTP / 1.1
>主机:localhost:8080
> User-Agent:curl / 7.45.0
>接受:* / *
> Content-Length:522
>预计:100-继续
>内容类型:multipart / form-data; boundary = --------------------------- a17d4e54fcec53f8
>
< HTTP / 1.1 301永久移动
<位置:/ submit /
<日期:2015年11月18日星期三14:48:38 GMT
< Content-Length:0
<内容类型:text / plain; charset = utf-8
*发送结束前发生HTTP错误,停止发送
<
*关闭连接0

我运行的控制台运行main.go 使用curl时不会输出任何内容。

解决方案

我设法解决了我的问题,所以在这里以防万一别人需要它。感谢@JiangYD使用curl来测试服务器。



TL; DR




  • 我写了 http.HandleFunc(/ submit /,submit)但我正在向 / submit (注意缺少的斜线)<<这一点很重要,因为重定向

  • 不要自己指定Content-Type,浏览器会为你做这件事



LONG ANSWER



我做了@JiangYD的说法并使用 curl来测试服务器,我更新了答案响应。我发现奇怪的是有一个301重定向,因为我没有把它放在那里,我决定使用下面的curl命令

  curl -v -F'uploadFile = @ \C:/Users/raul-/Desktop/test.png\'-L http:// localhost:8080 / submit 

(注意 -L )curl遵循重定向,尽管它再次失败,因为重定向时curl切换从POST到GET,但是通过该响应,我发现将 / submit 的请求重定向到 / submit / 我记得这就是我在 main 函数中编写它的方式。



修复后仍然失败,响应是 http:没有这样的文件,通过查看 net / http 代码,我发现它意味着该字段没有'所以我做了一个快速测试,对所有获得的字段名进行迭代:

  for k,_:= range r。 MultipartForm.File {
log.Println(k)
}

我将'uploadFile 作为字段名称,I删除了curl命令中的单引号,现在它完美地上传了文件。



但是它并没有在这里结束,我现在知道服务器工作正常,因为我可以上传一个使用 curl 的文件,但是当我尝试通过托管网页上传时,出现错误: Content-Type中没有multipart边界参数

$ b因此,我发现我假设在标题中包含边界,我将fetch更改为如下所示:

  fetch('/ submit',{
method:'post',
headers:{
Content-Type :multipart / form-data; border = ------------------------+ boundary
},body:formData})



我这样计算边界:

  var boundary = Math.random()。toString()。substr(2); 

出现错误: multipart:NextPart:EOF 那么您如何计算边界?我阅读spec https://html.spec.whatwg.org/multipage/forms.html#multipart/form-data-encoding-algorithm ,发现边界是由编码文件的算法计算出来的,在我的例子中是FormData,FormData API没有公开获得边界的方法,但是我发现浏览器添加了Content-如果你没有指定它,自动输入 multipart / form-data 和边界,所以我从 fetch 呼叫,现在它终于起作用了!


I've started playing with go very recently so I'm still a noob, sorry if I make too many mistakes. I've been trying to fix this for a long time but I just don't understand what's going on. In my main.go file I have a main function:

func main() {
    http.HandleFunc("/", handler)
    http.HandleFunc("/submit/", submit)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

The handler function looks like this:

func handler(w http.ResponseWriter, r *http.Request) {
    data, _ := ioutil.ReadFile("web/index.html")
    w.Write(data)
}

I know this is not the best way to serve a website The submit function looks like this:

func submit(w http.ResponseWriter, r *http.Request) {
    log.Println("METHOD IS " + r.Method + " AND CONTENT-TYPE IS " + r.Header.Get("Content-Type"))
    r.ParseMultipartForm(32 << 20)
    file, header, err := r.FormFile("uploadFile")
    if err != nil {
        json.NewEncoder(w).Encode(Response{err.Error(), true})
        return
    }
    defer file.Close()

    out, err := os.Create("/tmp/file_" + time.Now().String() + ".png")
    if err != nil {
        json.NewEncoder(w).Encode(Response{err.Error(), true})
        return
    }
    defer out.Close()

    _, err = io.Copy(out, file)
    if err != nil {
        json.NewEncoder(w).Encode(Response{err.Error(), true})
        return
    }

    json.NewEncoder(w).Encode(Response{"File '" + header.Filename + "' submited successfully", false})
}

The problem is when the submit function is executed, r.Method is GET and r.Header.Get("Content-Type") is an empty string, then it continues until the first if where r.FormFile returns the following error: request Content-Type isn't multipart/form-data I don't understand why r.Method is always GET and there's no Content-Type. I've tried to do the index.html in many different ways but r.Method is always GET and Content-Type is empty. Here's the function in index.html that uploads a file:

function upload() {
    var formData = new FormData();
    formData.append('uploadFile', document.querySelector('#file-input').files[0]);
    fetch('/submit', {
        method: 'post',
        headers: {
          "Content-Type": "multipart/form-data"
        },
        body: formData
    }).then(function json(response) {
        return response.json()
    }).then(function(data) {
        window.console.log('Request succeeded with JSON response', data);
    }).catch(function(error) {
        window.console.log('Request failed', error);
    });
}

And here's the HTML:

<input id="file-input" type="file" name="uploadFile" />

Note that the tag is not inside a tag, I thought that could be the problem so I changed both the function and the HTML to something like this:

function upload() {
    fetch('/submit', {
        method: 'post',
        headers: {
          "Content-Type": "multipart/form-data"
        },
        body: new FormData(document.querySelector('#form')
    }).then(function json(response) {
        return response.json()
    }).then(function(data) {
        window.console.log('Request succeeded with JSON response', data);
    }).catch(function(error) {
        window.console.log('Request failed', error);
    });
}

<form id="form" method="post" enctype="multipart/form-data" action="/submit"><input id="file-input" type="file" name="uploadFile" /></form>

But that didn't work neither. I've searched with Google how to use fetch() and how to receive a file upload from go and I've seen that they are pretty similar to mine, I don't know what I'm doing wrong.

UPDATE: After using curl -v -F 'uploadFile=@\"C:/Users/raul-/Desktop/test.png\"' http://localhost:8080/submit I get the following output:

* Trying ::1...
* Connected to localhost (::1) port 8080 (#0)
> POST /submit HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.45.0
> Accept: */*
> Content-Length: 522
> Expect: 100-continue
> Content-Type: multipart/form-data; boundary=---------------------------a17d4e54fcec53f8
>
< HTTP/1.1 301 Moved Permanently
< Location: /submit/
< Date: Wed, 18 Nov 2015 14:48:38 GMT
< Content-Length: 0
< Content-Type: text/plain; charset=utf-8
* HTTP error before end of send, stop sending
<
* Closing connection 0

The console where I'm running go run main.go outputs nothing when using curl.

解决方案

I managed to solve my problem, so here it is in case someone else needs it. And thanks @JiangYD for the tip of using curl to test the server.

TL;DR

  • I wrote http.HandleFunc("/submit/", submit) but I was making a POST request to /submit (note the missing slash) << This is important because of redirections
  • Don't specify the Content-Type yourself, the browser will do it for you

LONG ANSWER

I did as @JiangYD said and used curl to test the server, I updated my answer with the response. I found odd that there was a 301 Redirect since I didn't put it there, I decided to use the following curl command

curl -v -F 'uploadFile=@\"C:/Users/raul-/Desktop/test.png\"' -L http://localhost:8080/submit

(note the -L) That way curl followed the redirect, though it failed again because, when redirecting, curl switched from POST to GET but with that response I found out that the request to /submit was being redirected to /submit/ and I remembered that's how I wrote it in the main function.

After fixing that it still failed, the response was http: no such file and by looking at the net/http code I found that it meant the field didn't exist, so I did a quick test iterating over all the field names obtained:

for k, _ := range r.MultipartForm.File {
    log.Println(k)
}

I was getting 'uploadFile as the field name, I removed the single quotes in the curl command and now it uploaded the file perfectly

But it doesn't end here, I now knew the server was working correctly because I could upload a file using curl but when I tried uploading it through the hosted web page I got an error: no multipart boundary param in Content-Type.

So I found out I was suppose to include the boundary in the header, I changed fetch to something like this:

fetch('/submit', {
    method: 'post',
    headers: {
        "Content-Type": "multipart/form-data; boundary=------------------------" + boundary
    }, body: formData})

I calculate the boundary like this:

var boundary = Math.random().toString().substr(2);

But I still got an error: multipart: NextPart: EOF So how do you calculate the boundary? I read the spec https://html.spec.whatwg.org/multipage/forms.html#multipart/form-data-encoding-algorithm and found out the boundary is calculated by the algorithm that encodes the file, which in my case is FormData, the FormData API doesn't expose a way to get that boundary but I found out that the browser adds the Content-Type with multipart/form-data and the boundary automatically if you don't specify it so I removed the headers object from the fetch call and now it finally works!

这篇关于用go处理文件上传的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆