programming

17 How to Upload File in Golang

Upload Files

Apart from receiving data input in the form of forms and query params, we usually also need data input in the form of files from our users. Golang actually already has this feature to handle file upload management. This makes it easier for us if we create a website that can accept file input.

When we want to receive an uploaded file, we need to parse it first using Request.ParseMultipartFrom(size) or we can retrieve the file data using Request.FormFile(name) which automatically parses it first. The data contained in the multipart package such as multipart.File as the file representation and multipart.FileHeader as the file information.

Example of File Upload Implementation

Create an HTML template like the one below with the file name upload.form.html.

<!DOCTYPE html>
<html>
  <head>
    <title>Form</title>
  </head>
  <body>
    <form action="/upload" method="post" enctype="multipart/form-data">
      <label>Name : <input type="text" name="name"></label><br>
      <label>File : <input type="file" name="file"></label><br>
      <input type="submit" value="Upload">
    </form>
  </body>
</html>

After that, we create an upload handler form page like the one below.

func UploadFormHandler(w http.ResponseWriter, r *http.Request) {
	err := myTemplates.ExecuteTemplate(w, "upload.form.html", nil)
	if err != nil {
		panic(err)
	}
}

And add the handler function to the mux router so that the page is registered.

	mux.HandleFunc("/upload-form", UploadFormHandler)

Do a build again and run the program and it should appear as shown below.

tutorial golang redirect

Next, we have to create a handle function to capture POS, which is the process that will save the file uploaded by the user.

func UploadHandler(w http.ResponseWriter, r *http.Request) {
	file, fileHeader, err := r.FormFile("file")
	if err != nil {
		panic(err)
	}
	fileDestination, err := os.Create("./resources/" + fileHeader.Filename)
	if err != nil {
		panic(err)
	}
	_, err = io.Copy(fileDestination, file)
	if err != nil {
		panic(err)
	}
	name := r.PostFormValue("name")
	myTemplates.ExecuteTemplate(w, "upload.success.html", map[string]interface{}{
		"Name": name,
		"File": "/static/" + fileHeader.Filename,
	})
}

Make sure our handler is registered on the mux router.

mux.HandleFunc("/upload", UploadHandler)

After that, we also need to create a page template. After the upload action, it will return to the success page for creating the upload.success.html file as below.

<!DOCTYPE html>
<html>
  <head>
    <title>Success {{.Name}}</title>
  </head>
  <body>
    <h1>{{ .Name }}</h1>
    <a href="{{ .File }}">File</a>
  </body>
</html>

In the previous tutorial here, we set the files in the resources folder to be read on the /static path so that we can access the files in the resources folder by accessing the path page on a website like this.

http://localhost:8080/static/<nama-file>

Run the program then we try to upload any file to carry out testing as below.

tutorial golang redirect

The display after a successful upload is as below.

tutorial golang redirect

This is what it looks like when the file has been opened because the path is static and directed to the resources folder.

tutorial golang redirect

Create Unit Test Upload

How do I test the upload handler function? Here’s how we create unit tests when we create the upload handler.

//go:embed resources/tutorial-golang.webp
var uploadFileTest []byte

func TestUploadHandler(t *testing.T) {
	type args struct {
		name string
	}
	tests := []struct {
		name string
		args args
		want string
	}{
		{
			name: "success",
			args: args{
				name: "success upload",
			},
			want: "<!DOCTYPE html>\n<html>\n  <head>\n    <title>Success success upload</title>\n  </head>\n  <body>\n    <h1>success upload</h1>\n    <a href=\"/static/contoh-upload.jpg\">File</a>\n  </body>\n</html>",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			body := new(bytes.Buffer)

			writer := multipart.NewWriter(body)
			writer.WriteField("name", tt.args.name)
			file, _ := writer.CreateFormFile("file", "contoh-upload.jpg")
			file.Write(uploadFileTest)
			writer.Close()

			request := httptest.NewRequest(http.MethodPost, "http://localhost/upload", body)
			request.Header.Set("Content-Type", writer.FormDataContentType())
			recorder := httptest.NewRecorder()
			UploadHandler(recorder, request)

			bodyResponse, _ := io.ReadAll(recorder.Result().Body)
			bodyString := string(bodyResponse)

			if !reflect.DeepEqual(bodyString, tt.want) {
				t.Errorf("response = %#v, want = %#v\n", bodyString, tt.want)
			}
		})
	}
}
comments powered by Disqus