Upload File
Selain menerima input data berupa form dan query params, biasanya kita juga membutuhkan input data berupa file dari pengguna kita. Golang sebenarnya sudah memiliki fitur tersebut untuk menangani management upload file tersebut. Hal ini lebih memudahkan kita jika kita membuat web yang bisa menerima input file.
Saat kita ingin menerima upload file kita perlu melakukan parsing terlebih dahulu menggunakan Request.ParseMultipartFrom(size)
atau bisa ambil data file tersebut menggunakan Request.FormFile(name)
yang mana didalamnya secara otomatis melakukan parsing terlebih dahulu. Data-data yang terdapat pada package multipart
seperti multipart.File
sebagai representasi file-nya dan multipart.FileHeader
sebagai informasi file-nya.
Contoh Implementasi Upload File
Buatlah template HTML seperti dibawah ini dengan nama file 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>
Setelah itu kita buat halaman form upload handler seperti dibawah ini.
func UploadFormHandler(w http.ResponseWriter, r *http.Request) {
err := myTemplates.ExecuteTemplate(w, "upload.form.html", nil)
if err != nil {
panic(err)
}
}
Dan tambahkan fungsi handler tersebut pada router mux
agar halaman terregister.
mux.HandleFunc("/upload-form", UploadFormHandler)
Lakukan build
ulang dan jalankan program maka harusnya akan muncul seperti tampilan dibawah ini.
Selanjutnya kita harus buat fungsi hanlder untuk menangkap POST
yang mana proses ini yang akan menyimpan file yang di upload oleh pengguna.
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,
})
}
Pastikan handler kita sudah didaftarkan pada router mux
.
mux.HandleFunc("/upload", UploadHandler)
Setelah itu kita perlu buat juga template halaman setelah aksi upload tersebut akan mengembalikan ke halaman sukses buat file upload.success.html
seperti dibawah ini.
<!DOCTYPE html>
<html>
<head>
<title>Success {{.Name}}</title>
</head>
<body>
<h1>{{ .Name }}</h1>
<a href="{{ .File }}">File</a>
</body>
</html>
Pada tutorial sebelumnya disini, kita sudah melakukan setting agar file yang terdapat pada folder resources
bisa terbaca pada path /static
sehingga kita bisa mengakses file yang ada di folder resources
dengan mengakses halaman path pada website seperti ini.
http://localhost:8080/static/<nama-file>
Jalankan program lalu kita coba upload file sembarang untuk melakukan testing seperti dibawah ini.
Tampilan setelah sukses dilakukan upload seperti dibawah ini.
Ini tampilan ketika file sudah dibuka karena path tersebut static
dan diarahkan ke dalam folder resources
.
Membuat Unit Test Upload
Bagaimana cara melakukan pengetesan dari fungsi handler upload tersebut? Berikut ini cara kita membuat unit test saat kita buat handler upload-nya.
//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)
}
})
}
}