Golang으로 웹서버 만들기(3)

김영한·2020년 11월 11일
0
post-custom-banner

코드 Github
참고


📢 FILE upload 해보기

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 핸들러를 만들지 않아서 에러가 발생하는 것이다.


🔔 이제 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)      // 어디에 업로드되는지 출력
...
}


저장되었으므로 저장된 경로메시지가 출력되고


해당 경로에 잘 저장되어 있는 것을 확인할 수 있다!


🔔 2탄에서 했던 작업처럼 이제 테스트 코드로 만들어보자

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되는 것을 확인할 수 있다.

post-custom-banner

0개의 댓글