WEB4 폴더에 작성했다.
func main() {
// public 경로를 serving하는 파일서버
http.Handle("/", http.FileServer(http.Dir("public")))
http.ListenAndServe(":3000", nil)
}
static 웹 서버를 만들 것인데 WEB4 안에 public이라는 폴더를 만들어준다.
즉 public폴더 안에 있는 파일들을 access할 수 있는 웹 서버를 열어주는 것이다.
public 폴더 안에 index.html 파일을 생성해준다.
<html>
<head>
<title>
Go로 만드는 웹 4
</title>
</head>
<body>
<p><h1>파일을 전송해보자.</h1></p>
<form action="/uploads" method="POST" accept-charset="utf-8" enctype="multipart/form-data">
<p><input type="file" id="upload_file" name="upload_file"></p>
<p><input type="submit" name="upload"></p>
</form>
</body>
</html>
웹 브라우저로 실행해보면 다음과 같은 화면을 얻을 수 있다.
아무 파일이나 선택해서 제출해하면
에러가 발생하는데 upload 핸들러를 만들지 않아서 에러가 발생하는 것이다.
다시 main파일로 돌아와서 첫 게시물에 했던 것처럼 핸들러를 작성해준다.
func uploadsHandler(w http.ResponseWriter, r *http.Request) {}
func main() {
...
http.HandleFunc("/uploads", uploadsHandler)
...
}
전송된 파일은 Request에 실려서 오게되는데 FormFile
을 이용해서 읽어준다.
func uploadsHandler(w http.ResponseWriter, r *http.Request) {
uploadFile, header, err := r.FormFile("upload_file") // id가 upload_file이다.
if err != nil { // 에러 제어
w.WriteHeader(http.StatusBadRequest)
fmt.Fprint(w, err)
return
}
// defer는 함수가 종료되기 직전에 실행됨
defer uploadFile.Close() // 파일을 만들고 닫아줘야함(os자원이라 반납해야함)
}
이제 받은 파일을 폴더에 저장을 해야한다.
WEB4 폴더 안에 uploads라는 폴더를 하나 만들어준다.
func uploadsHandler(w http.ResponseWriter, r *http.Request) {
...
dirname := "./uploads"
os.MkdirAll(dirname, 0777) // dirname 폴더가 없으면 만들어줌, 777 -> read,write,execute 가능
filepath := fmt.Sprintf("%s/%s", dirname, header.Filename) // 폴더명/파일명, 파일명은 header에 들어있다.
file, err := os.Create(filepath) // 비어있는 새로운 파일을 만듬
defer file.Close() // 파일을 만들고 닫아줘야함(os자원이라 반납해야함)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
fmt.Fprint(w, err)
return
}
io.Copy(file, uploadFile) // 비어있는 파일에 uploadFile을 복사해준다.
w.WriteHeader(http.StatusOK) // err없이 잘 되면 OK신호
fmt.Fprint(w, filepath) // 어디에 업로드되는지 출력
...
}
저장되었으므로 저장된 경로메시지가 출력되고
해당 경로에 잘 저장되어 있는 것을 확인할 수 있다!
WEB4 폴더에 main_test.go
파일을 생성해준다.
cmd를 키고 WEB4 경로까지 들어와서 goconvey를 실행해준다.
func TestUploadTest(t *testing.T) {
assert := assert.New(t)
path := "C:/Users/User/go/src/github.com/soosungp33/Golang_web/WEB4/test.jpg" // 사진 파일을 따로 저장해둬야함
file, _ := os.Open(path) // 경로에 있는 파일을 연다.
defer file.Close()
os.RemoveAll("./uploads") // uploads폴더안에 있는 파일들을 전부 지워준다.
// 이 테스트가 실행되면 다시 생기게끔
buf := &bytes.Buffer{}
// NewWriter는 iowriter를 넘겨줘야하는데 buffer형식이다.
writer := multipart.NewWriter(buf) // 웹으로 파일 데이터를 전송할 때 사용하는 포멧, 데이터는 buf에 실려있다.
// Form파일을 하나 만들어줌, Base()는 경로에서 filename만 자른다.(123.JPG)
multi, err := writer.CreateFormFile("upload_file", filepath.Base(path))
assert.NoError(err)
io.Copy(multi, file)
writer.Close()
// 이제 준비는 다 했고 웹 서버를 콜해보자
res := httptest.NewRecorder()
req := httptest.NewRequest("POST", "/uploads", buf)
// 이 데이터가 어떤 데이터인지 알려줘야함
req.Header.Set("Content-type", writer.FormDataContentType())
uploadsHandler(res, req) // main.go에 있는 핸들러 메소드
assert.Equal(http.StatusOK, res.Code)
}
PASS되는 것을 볼 수 있고 test.jpg파일이 uploads 폴더에 생긴 것을 볼 수 있다!
실제로 이 파일이 같은 것인지 확인해보자
func TestUploadTest(t *testing.T) {
...
// 실제 폴더에 파일이 들어있는지 확인
uploadFilePath := "./uploads/" + filepath.Base(path) // 업로드한 파일 경로
_, err = os.Stat(uploadFilePath) // err가 위에서 이미 선언되었기 대문에 선언대입문(:=) 대신 일반대입문(=)을 사용한다.
assert.NoError(err) // 이걸 통과하면 파일이 있다는 뜻
// 파일이 있다면 업로드된 파일하고 기존 파일하고 같은지 확인해야함.
uploadFile, _ := os.Open(uploadFilePath) // 업로드한 파일 경로
originFile, _ := os.Open(path) // 기존 파일 경로
defer uploadFile.Close()
defer originFile.Close()
uploadData := []byte{}
originData := []byte{}
uploadFile.Read(uploadData)
originFile.Read(originData)
assert.Equal(originData, uploadData) // 기존 파일과 업로드 파일이 같은지 검사
...
}
PASS되는 것을 확인할 수 있다.