Control file downloads with go

I started playing with leaving just recently, so I'm still a noob, sorry if I make too many mistakes. I tried to fix this for a long time, but I just do not understand what is happening. 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 is as follows:

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

I know that 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 to the first if 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 is no Content-Type. I tried to make index.html in different ways, but r.Method is always GET and Content-Type is empty. Here is the function in index.html that loads the 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 is the HTML:

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

Please note that the tag is not inside the tag, I thought it might be a 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 did not work. I searched on Google how to use fetch () and how to get the files to be downloaded from the site, and I saw that they are very 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 in which I run go run main.go does not display anything when using curl.

+5
source share
1 answer

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

TL DR

  • I wrote http.HandleFunc("/submit/", submit) , but I made a POST request to /submit (note the missing slash) <<This is important because of the redirect.
  • Do not specify Content-Type yourself, the browser will do it for you

LONG RESPONSE

I did this as @JiangYD and used curl to test the server , I updated my answer with the answer. It seemed strange to me that there is 301 Redirect, since I did not add it, 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 ). So curl followed the redirection, although it failed again because the curl switched from POST to GET when redirecting, but with this answer I found out that the /submit request was redirected to /submit/ , and I remembered how I wrote it in the main function.

After fixing that it still failed, the answer was http: no such file , and looking at the net/http code, I found that it means that the field does not exist, so I did a quick test, iterating over all the received field names

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

I got 'uploadFile as the field name, I removed the single quotes in the curl command, and now it downloaded the file perfectly

But it does not end here, now I knew that the server is working correctly, because I can upload the file using curl , but when I tried to upload it through a hosted web page, I received an error message: no multipart boundary param in Content-Type .

So, I found out that I had to include the border 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 compute the border as follows:

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

But I still have an error: multipart: NextPart: EOF So how do you calculate the border? I read the specification https://html.spec.whatwg.org/multipage/forms.html#multipart/form-data-encoding-algorithm and found out that the border is computed by an algorithm that encodes a file, which in my case is FormData, FormData The API does not provide a way to get this border, but I found out that the browser automatically adds a Content-Type with multipart/form-data and a border, unless you specify it so that I remove the header object from the fetch call and now it finally works!

+6
source

Source: https://habr.com/ru/post/1236204/


All Articles