출처: https://qiita.com/D3vel0pper/items/ea9f943c5e5d22a13fb6 

Go 언어는 Web 응용 프로그램을 만들 때 핸들러를 등록하여 해당 핸들러에 등록된 작업을 수행하는 구조로 되어있다.

만들 프로그램은 다음과 같은 동작을 생각하고 있다..

  1. 우선 업로드 화면보기 (upload.html)
  2. action에서 다른 핸들러 처리를 전달 (/save를 지정)
  3. 통과된는 파일의 저장 처리를 실시 (/ save)
  4. 마지막으로 업로드 화면으로 돌아가기 (리다이렉트)

한편, 오류가 발생했을 때 오류 페이지를 보낼 생각이으로 오류 페이지를 다룬다면,

  1. 업로드 화면
  2. 보존 처리
  3. 오류 페이지

3 개의 처리기가 필요하다.

코드 만들어 보기

템플리트를 제공하는 html 파일은 여기

upload.html

<!DOCTYPE html>
<!-- template용 html 파일 -->
<html>
<head>
   
<title>파일 업로드 테스트</title>
</head>
<body>
<div class="container">
   
<h1>파일 업로드 테스트</h1>
   
<form method="post" action="/save" enctype="multipart/form-data">
       
<fieldset>
           
<input type="file" name="upload_files" id="upload_files" multiple="multiple">
           
<input type="submit" name="submit" value="업로드 시작">
       
</fieldset>
   
</form>
</div>
</body>
</html>

main.go은 이런 느낌

main.go

package main

import (
   
"html/template"
   
"io"
   
"net/http"
   
"os"
   
"fmt"
)

//「/save」용 핸들러
func saveHandler(w http.ResponseWriter, r *http.Request) {
   
//MultipartReader를 이용해서 받은 파일을 읽는다
   reader, err := r.MultipartReader()

   
//에러가 발생하면 던진다
   
if err != nil {
       http.Error(w, err.Error(), http.StatusInternalServerError)
       
return
   }

   
//for로 복수 파일이 있는 경우에 모든 파일이 끝날때까지 읽는다
   
for {
       part, err := reader.NextPart()
       
if err == io.EOF {
           
break
       }

       
//파일 명이 없는 경우는 skip
       
if part.FileName() == "" {
           
continue
       }

       
//uploadedfile 디렉토리에 받았던 파일 명으로 파일을 만든다
       uploadedFile, err := os.Create(
"<업로드ㅎ 하고 싶은 장소의 path>" + part.FileName())
       
if err != nil {
           http.Error(w, err.Error(), http.StatusInternalServerError)
           uploadedFile.Close()
           redirectToErrorPage(w,r)
           
return
       }

       
//만든 파일에 읽었던 파일 내용을 모두 복사
       _, err = io.Copy(uploadedFile, part)
       
if err != nil {
           http.Error(w, err.Error(), http.StatusInternalServerError)
           uploadedFile.Close()
           redirectToErrorPage(w,r)
           
return
       }
   }
   
//upload 페이지에 리다이렉트
   http.Redirect(w,r,
"/upload",http.StatusFound)
}

//「/upload」용 핸들러
func uploadHandler(w http.ResponseWriter, r *http.Request) {
   
var templatefile = template.Must(template.ParseFiles("<template파일로의 full path>"))
   templatefile.Execute(w,
"upload.html")
}

//「/errorPage」용 핸들러
func errorPageHandler(w http.ResponseWriter, r *http.Request) {
   fmt.Fprintf(w,
"%s","<p>Internal Server Error</p>")
}

//error가 발생했을 때에 에러 페이지로 이동한다
func redirectToErrorPage(w http.ResponseWriter, r *http.Request) {
   http.Redirect(w,r,
"/errorPage",http.StatusFound)
}

func main() {
   
//핸들러 등록
   http.HandleFunc(
"/upload", uploadHandler)
   http.HandleFunc(
"/save",saveHandler)
   http.HandleFunc(
"/errorPage",errorPageHandler)
   
//서버 시작
   http.ListenAndServe(
":8080", nil)
}

main.go의 <~~ path>라는 부분은 적절하게 자신의 환경에 맞게 변경해야 한다.

<p> 라든가 </ p>는 html 태그이기 때문에 바꾸지 않아도 괜찮다.

(※ 예를 들어 "C:/Go/MyAppTest/" 등)

실행 후 http://localhost:8080/upload 에 액세스하여 업로드 할 파일을 선택하고 업로드 시작을 누르면 업로드 할 수 있다.