我最近刚开始玩go,所以我还是菜鸟,对不起,如果我犯了太多错误。我已经尝试解决了很长时间,但我只是不明白发生了什么。在我的main.go文件中,我有一个主要功能:
func main() { http.HandleFunc("/", handler) http.HandleFunc("/submit/", submit) log.Fatal(http.ListenAndServe(":8080", nil)) }
处理程序函数如下所示:
func handler(w http.ResponseWriter, r *http.Request) { data, _ := ioutil.ReadFile("web/index.html") w.Write(data) }
我知道这不是服务网站的最佳方法提交功能如下所示:
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}) }
问题是,当执行Submit 函数时,它r.Method是GET并且r.Header.Get("Content- Type")是一个空字符串,如果r.FormFile返回以下错误,它将继续执行直到第一个: request Content-Type isn't multipart/form-data 我不明白为什么r.Method总是GET并且没有Content- 类型。我试图以许多不同的方式来做index.html,但是r.Method始终是GET,而Content- Type为空。这是index.html中上传文件的函数:
r.Method
GET
r.Header.Get("Content- Type")
request Content-Type isn't multipart/form-data
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); }); }
这是HTML:
<input id="file-input" type="file" name="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('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>
但这也不起作用。我在Google上搜索了如何使用fetch()以及如何从go接收文件上传,我发现它们与我的非常相似,我不知道我做错了什么。
更新: 使用后,curl -v -F 'uploadFile=@\"C:/Users/raul-/Desktop/test.png\"' http://localhost:8080/submit我得到以下输出:
curl -v -F 'uploadFile=@\"C:/Users/raul-/Desktop/test.png\"' http://localhost:8080/submit
* 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
go run main.go使用curl时,我运行的控制台没有任何输出。
go run main.go
我设法解决了我的问题,所以这里是万一有人需要的情况。还要感谢@JiangYD提供了使用curl测试服务器的技巧。
http.HandleFunc("/submit/", submit)
/submit
我像@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功能上。
/submit/
main
修复它仍然失败后,响应是http: no such file,通过查看net/http代码,我发现它意味着该字段不存在,因此我对访问的所有字段名称进行了快速测试:
http: no such file
net/http
for k, _ := range r.MultipartForm.File { log.Println(k) }
我得到的'uploadFile是字段名称,我删除了curl命令中的单引号,现在它完美地上传了文件
'uploadFile
但这还没有结束,我现在知道服务器可以正常工作,因为我可以使用来上传文件,curl但是当我尝试通过托管网页上传文件时,出现了错误:no multipart boundary param in Content-Type。
curl
no multipart boundary param in Content-Type
因此,我发现我想在标头中包含边界,因此将fetch更改为以下内容:
fetch('/submit', { method: 'post', headers: { "Content-Type": "multipart/form-data; boundary=------------------------" + boundary }, body: formData})
我这样计算边界:
var boundary = Math.random().toString().substr(2);
但是我仍然有一个错误:multipart: NextPart: EOF那么如何计算边界?我阅读了规范https://html.spec.whatwg.org/multipage/forms.html#multipart/form- data-encoding- algorithm,发现边界是由对文件进行编码的算法计算得出的,在我的情况下是FormData,FormData API并未提供获取该边界的方法,但我发现浏览器会自动将Content-Type与multipart/form- data和添加边界(如果未指定),因此我从fetch调用中删除了标头对象,现在终于可以了!
multipart: NextPart: EOF
multipart/form- data
fetch