don't stop believing

Upload files 본문

Golang/Gin

Upload files

Tongchun 2019. 2. 27. 19:04

이번에는 파일 업로드를 해보겠습니다.

https://github.com/gin-gonic/gin#upload-files


파일 하나 올리는 것과, 여러개를 동시에 올리는 것을 해보겠습니다.

우선 파일을 올릴 수 있는 html 파일을 아래와 같이 만들어 줍니다. 저는 uploadfiles.html로 만들었습니다.

위치는 templates 폴더 안에 위치합니다.

1
2
3
4
5
6
7
8
9
10
11
12
<html>
<head>
<title>Tongchun's go gin</title>
</head>
<body>
<h2>page: {{ .page }}</h2>
<form action="http://localhost:8080/upload" method="POST" enctype="multipart/form-data">
upload file: <input type="file" name="file">
<input type="submit" value="Submit">
</form>
</body>
</html>
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

이번에는 server.go 내용입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package main
import (
"fmt"
"log"
"net/http"
"path/filepath"
"github.com/gin-gonic/gin"
)
func setupRouter() *gin.Engine {
// Disable Console Color
// gin.DisableConsoleColor()
r := gin.Default()
r.GET("/uploadpage", func(c *gin.Context) {
title := "upload single file"
c.HTML(http.StatusOK, "uploadfile.html", gin.H{
"page": title,
})
})
r.POST("/upload", uploadSingle)
return r
}
func uploadSingle(c *gin.Context) {
// single file
file, err := c.FormFile("file")
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("get form err: %s", err.Error()))
return
}
log.Println(file.Filename)
// Upload the file to specific dst.
filename := filepath.Base(file.Filename)
uploadPath := "./example/upload/" + filename
log.Println(filename)
if err := c.SaveUploadedFile(file, uploadPath); err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error()))
return
}
c.JSON(200, gin.H{
"status": "posted",
"file name": file.Filename,
})
}
func main() {
r := setupRouter()
// Set a lower memory limit for multipart forms (default is 32 MiB)
r.MaxMultipartMemory = 8 << 20 // 8 MiB
r.Static("/files", "./example/upload")
r.LoadHTMLGlob("./example/templates/*")
// Listen and Server in 0.0.0.0:8080
r.Run(":8080")
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

먼저 main() 함수에 아래와 같이 MaxMultipartMemory를 설정합니다. 

// Set a lower memory limit for multipart forms (default is 32 MiB)

r.MaxMultipartMemory = 8 << 20 // 8 MiB

비트 이동과 메모리 설정의 경우 아래 포스트를 참고할 수 있습니다.

https://dejavuqa.tistory.com/328


setupRouter() 함수안에 파일 업로드를 할수 있는 GET method의 경로를 추가합니다.

r.GET("/uploadpage", func(c *gin.Context) {

    title := "upload single file"

    c.HTML(http.StatusOK, "uploadfile.html", gin.H{

        "page": title,

    })

})

server.go를 실행하고 localhost:8080/uploadpage 를 호출하면 파일을 업로드 할 수 있는 페이지가 호출됩니다.

html 페이지에서 submit 버튼을 클릭하면 loalhost:8080/upload 로 파일을 업로드 하게 됩니다.


/upload 경로의 handler 함수로 uploadSingle() 함수가 호출됩니다.

업로드된 파일은 설정된 경로에 파일 이름 그대로 저장이 됩니다.


실행해 보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ go run example/server.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /uploadpage --> main.setupRouter.func1 (3 handlers)
[GIN-debug] POST /upload --> main.uploadSingle (3 handlers)
[GIN-debug] GET /files/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (3 handlers)
[GIN-debug] HEAD /files/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (3 handlers)
[GIN-debug] Loaded HTML Templates (4):
- note.html
- uploadfile.html
-
- index.html
[GIN-debug] Listening and serving HTTP on :8080
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

localhost:8080/uploadpage 를 호출하고 업로드할 파일을 선택합니다.

Submit을 클릭해 파일을 업로드 합니다.

파일이 재대로 올라갔는지 서버에서도 확인해 봅니다.

1
2
3
4
5
$ ll ./example/upload/
total 37136
drwxr-xr-x 4 tongchunkim staff 128B 2 28 16:46 .
drwxr-xr-x 6 tongchunkim staff 192B 2 27 16:37 ..
-rw-r--r-- 1 tongchunkim staff 34K 2 28 16:46 traffic_control.zip
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX


이번에는 파일 3개와 Post Form 데이터를 동시에 받아보겠습니다.

먼저 아래와 같이 html 파일을 수정합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<html>
<head>
<title>Tongchun's go gin</title>
</head>
<body>
<h2>page: {{ .page }}</h2>
<form action="http://localhost:8080/multiupload" method="POST" enctype="multipart/form-data">
first name: <input type="text" name="first_name"><br>
family name: <input type="text" name="family_name"><br>
upload file 1: <input type="file" name="file"><br>
upload file 2: <input type="file" name="file"><br>
upload file 3: <input type="file" name="file"><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

확인할 사항은 file을 넘기는 input의 name을 file이라고 모두 동일하게 합니다.

그러면 3개의 file 데이터는 슬라이스 타입으로 전달되게 됩니다.


그리고 server.go 에서는 아래와 같이 route를 추가했습니다.

r.POST("/multiupload", uploadMultifile)


uploadMultifile 핸들러 함수는 아래와 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
func uploadMultifile(c *gin.Context) {
firstName := c.PostForm("first_name")
familyName := c.PostForm("family_name")
// Multipart form
form, err := c.MultipartForm()
if err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("get form err: %s", err.Error()))
return
}
files := form.File["file"]
log.Println(form)
for _, file := range files {
log.Println(file.Filename)
// Upload the file to specific dst.
filename := filepath.Base(file.Filename)
uploadPath := "./example/upload/" + filename
// Upload the file to specific dst.
if err := c.SaveUploadedFile(file, uploadPath); err != nil {
c.String(http.StatusBadRequest, fmt.Sprintf("upload file err: %s", err.Error()))
return
}
}
c.JSON(200, gin.H{
"status": "posted",
"file lise": files,
"file count": len(files),
"first name": firstName,
"family name": familyName,
})
}
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

server.go를 실행합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ go run example/server.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /uploadpage --> main.setupRouter.func1 (3 handlers)
[GIN-debug] POST /upload --> main.uploadSingle (3 handlers)
[GIN-debug] POST /multiupload --> main.uploadMultifile (3 handlers)
[GIN-debug] GET /files/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (3 handlers)
[GIN-debug] HEAD /files/*filepath --> github.com/gin-gonic/gin.(*RouterGroup).createStaticHandler.func1 (3 handlers)
[GIN-debug] Loaded HTML Templates (4):
- index.html
- note.html
- uploadfile.html
-
[GIN-debug] Listening and serving HTTP on :8080
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

uploadpage를 열고 post 데이터와 파일을 선택합니다.

submit을 클릭하면 지정한 json 데이터가 리턴됩니다.

지정된 경로에는 파일이 업로드되어 있습니다.

1
2
3
4
5
6
7
8
ll ./example/upload/
total 2056
drwxr-xr-x 6 tongchunkim staff 192B 2 28 18:28 .
drwxr-xr-x 6 tongchunkim staff 192B 2 27 16:37 ..
-rw-r--r-- 1 tongchunkim staff 34K 2 28 16:46 traffic_control.zip
-rw-r--r-- 1 tongchunkim staff 321K 2 28 18:28 ᅵᆫᅣᆺ 2019-02-28 ᅮ 4.45.41.png
-rw-r--r-- 1 tongchunkim staff 310K 2 28 18:28 ᅵᆫᅣᆺ 2019-02-28 ᅮ 4.46.50.png
-rw-r--r-- 1 tongchunkim staff 356K 2 28 18:28 ᅵᆫᅣᆺ 2019-02-28 ᅮ 6.00.06.png
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

추가로 main() 함수에는 Static file 경로를 지정했습니다.

r.Static("/files", "./example/upload")


localhost:8080/files/ 하위 경로에 업로드한 파일을 추가하면 다운받을 수 있습니다.

http://localhost:8080/multiupload/traffic_control.zip


Gin을 이용한 파일 업로드였습니다.


'Golang > Gin' 카테고리의 다른 글

[swag] Gin에서 Swagger 사용하기  (0) 2019.03.04
Grouping routes  (0) 2019.03.04
Map as querystring or postform parameters  (0) 2019.02.27
Another example: query + post form  (0) 2019.02.15
Multipart/Urlencoded Form  (1) 2019.02.14
Comments