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 폴더 안에 위치합니다.

<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>

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

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")
}

먼저 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() 함수가 호출됩니다.

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


실행해 보겠습니다.

$ 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

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

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

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

$ 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


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

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

<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>

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

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


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

r.POST("/multiupload", uploadMultifile)


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

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, }) }

server.go를 실행합니다.

$ 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

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

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

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

 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

추가로 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