파일을 업로드 하는 방법을 알아 본다.
업로드 하려면 먼저 로그인이 돼 있어야 한다. 깃허브로 로그인 한다. 그리고 input
을 추가한다.
block content
if errorMessage
span=errorMessage
form(method="POST")
label(for="avatar") Avatar
input(type="file", id="avatar", name="avatar",accept="image/*")
change-password
가 아니라 edit-profile
에서 추가해준다. type="file"
인 input
을 추가한다.
Avatar
라는 label
을 먼저 만들고 label(for="avatar")
를 입력하고 input
에 id="avatar"
를 추가 해준다.
이 두개가 합쳐져서 클릭하면 아바타를 선택할수 있다. 그리고 avatar
에 name
을 줘야 한다.
여전히 form
에 name
을 줘야 하기 때문이다. name="avatar"
라고 해준다.
사용자가 파일을 선택할수 있는데 pdf
같은 파일을 선택 할수도 있다.
모든 이미지 파일만 받아 들일수 있게 해준다. accept="image/*"
를 사용 하면 된다.
어떤 이미지 파일이든 선택할수 있게 되었다.
input
을 만드는게 첫 단계이다.
두번째는 이것을 도와줄
middleware
를 사용한다.
여기 multer
라는 middleware
가 있다.
https://www.npmjs.com/package/multer
multer
는 파일을 업로드 할수 있게 해준다.npm i multer
로 설치해준다.
그리고 multer
를 사용법을 알아 본다.
NOTE: Multer will not process any form which is not multipart (multipart/form-data).
"multer
는 multipart
가 아닌 form
을 처리하지 않는다." 라고 되있다.
multer
의 도움을 받고 싶다면 form
을 multipart form
으로 만들어야 한다.
form
에다가 enctype="multipart/form-data"
를 추가해준다.
block content
if errorMessage
span=errorMessage
form(method="POST", enctype="multipart/form-data")
이렇게 해준건 form
이 다르게 encode
된다는 의미이다.
이게 파일을 업로드 하기 위한 유일한 조건이다. form
을 multipart/form-data
로 만들어 주면 된다.
이게 파일을 백엔드로 보내기 위해 필요한 encoding type(enctype)
이다.
그리고 다음은
middleware
를 만들어 줘야 한다.
const upload = multer({ dest: 'uploads/' })
app.post('/profile', upload.single('avatar'), function (req, res, next) {
// req.file is the `avatar` file
// req.body will hold the text fields, if there were any
})
middleware
를 설정 해주고 이 경우에는 설정과 함께 multer
함수를 사용해서 middleware
를 만들고 route
에서 사용하는 거다.
먼저 middleware
를 만들어 본다. middleware.js
에서
export const uploadFiles = multer({ dest: "uploads/" });
여기에서는 req
,res
를 쓰지 않는다. 대신 multer()
를 쓰고 설정한다.
multer
에는 configuration object
가 있다.
살펴보면 보낼수 있는것 중 하나는 destination
이다. 파일을 어디에 보낼지 정한다.
사용자가 파일을 보내면 사용자로부터 파일을 받으면 그 파일을 어딘가에 저장을 해야한다.
예를 들면 하드드라이브 같은 곳 말이다. 나중에는 이게 좋지 않은 이유를 알아 보고
다른 곳으로 저장 시킬거다.
그리고 잊지 말고
multer
를import
한다.
현재는 파일들을 하드드라이브에 저장해야 된다고 가정하고 진행한다.
const upload = multer({ dest: 'uploads/' })
이 경우에 uploads
라는 폴더를 만들고 그리고 multer
에게 사용자가 업로드 하는 모든 파일들을 서버의 uploads
폴더에 저장하라고 하는거다.
그래서 multer({ dest: "uploads/" })
라고 해주었다.
사용자가 보낸 파일을 uploads
폴더에 저장하도록 설정된 middleware
가 생겼다.
아직 그 폴더가 없어서 에러가 날거다. 폴더를 만들어 준다. (강의에서는 생기던데...)
이제
middleware
를route
에 사용한다.controller
에는 사용하지 않는다.
route
로 이동한다. 그리고 edit-profile
에서 사용 해준다.
get
이 아니라 post
에 사용해 준다. 사용방법을 살펴보면
app.post('/profile', upload.none(), function (req, res, next) {
// req.body contains the text fields
})
middleware
를 쓰고 controller
함수를 사용하는거다.
다시 설명하면 url
이 있고 middleware
, 그리고 controller
함수를 쓰는 거다.
이미 url
이 있고 post
에서 middleware
를 사용한다. 그리고 controller
함수를 쓰면 된다.
먼저 uploadFiles middleware
를 사용한다.
userRouter.get("/logout", protectorMiddleware, logout);
userRouter
.route("/edit")
.all(protectorMiddleware)
.get(getEdit)
.post(uploadFiles.single("avatar"), postEdit);
uploadFiles.
을 써주고 fields
,none
,single
,array
등이 있다.
사용자가 다수의 파일을 보내야 될때도 있어서 그렇다.
현재는 하나의 파일만 필요하니 single
로 사용해 준다. 그리고 filedName
을 써줘야 한다.
formt
의 어디에서 파일이 오고 있냐면 avatar
에서 오고 있다. 그래서 avatar
를 넣어 주었다. 그리고 string
이여야 한다.
조금 헷갈릴수 있는데 middleware
를 실행한 다음 postEdit
을 실행 하는거다.
왜 이렇게 하냐면 multer
는 input
으로 avatar
파일을 받아서 그 파일을 uploads
폴더에 저장한 다음 그 파일 정보를 postEdit
에 전달해 주는거다.
middleware
는 왼쪽에서 오른쪽 순서로 작동한다는 걸 기억한다.
multer middleware
가 먼저 실행되고 그 다음 postEdit
이 실행되는 거다.
다시 한번 정리하면 uploadFiles.single
이 하는 역할은 template
의 input
에서
오는 avatar
파일을 가지고 파일을 업로드 하고 uploads
폴더에 저장한다.
그리고 다음 controller
인 postEdit
에 그 파일의 정보를 전달하는 거다.
좋은점은 이렇게 하면 request
에 req.file
이 추가 된다는거다.
function (req, res, next) {
// req.file is the `avatar` file
현재는 이미 form data
인 req.body
를 알고 있다. 이제 이걸 사용하기에
userRouter.get("/logout", protectorMiddleware, logout);
userRouter
.route("/edit")
.all(protectorMiddleware)
.get(getEdit)
.post(uploadFiles.single("avatar"), postEdit);
이 코드가 req.file
을 사용하게끔 해주는 거다.
사용자가 url
로 form
을 보내면 middleware
를 걸쳐서 middleware
는 사진을 시스템에 저장하고 , req.file
을 추가 할거다.
그리고 나서 controller
를 실행시키면 req.file
과 req.body
를 사용 할수 있다.
현재 controller
는 이미 req.body
를 사용하고 있다.
하지만 아직 req.file
이 어떻게 생겼는지는 모른다.
export const postEdit = async (req, res) => {
const {
session: {
user: { _id },
},
body: { name, email, username, location },
file,
} = req;
console.log(file);
콘솔을 이용해서 확인해 본다. 에러가 없다. 아직 파일을 사용하고 있지 않지만
있어서 나쁠건 없다. 파일을 업로드 하면 어떤일도 발생하지 않지만 콘솔을 확인해 보면
file
을 볼수 있다. 파일 경로도 나온다. 파일명은 랜덤은로 만들어 졌다.
원본 파일명도 나온다. type
도 있고, destination
도 있고, 파일명과 경로로 있다.
이제 uploads
폴더를 확인해 본다. 살펴보면 현재 파일은 jpeg
같은 확장자가 없다.
그래도 브라우저는 이해하기 때문에 상관없다. 파일을 업로드 하는데 성공 했다.
req.file
을 사용할수 있는 이유는 userRouter
를 보면 postEdit
전에 Multer
를 사용하고 있다. 반대였다면 postEdit
에서 req.file
을 사용할수 없다.
middleware
의 순서는 아주 중요하다.
userRouter.get("/github/start", publicOnlyMiddleware, startGithubLogin);
userRouter.get("/github/finish", publicOnlyMiddleware, finishGithubLogin);
예를 들어 사용자가 /github/start
에 가면 먼저 middleware
가 확인하고 다음으로 가는 거랑 같은 거다.
이것도 마찬가지이다. middleware
를 확인하고 finishGithubLogin
으로 가는거다.
다시 한번 정리하면 single
은 하나의 파일만 업로드 한다는거고 파일의 input name
을 middleware
에 전달해야 한다.
name
은 form
의 name
이랑 같아야 한다.
path: 'uploads/2ec71c2270a855b3e422a370e005d5b3'
이제 경로(path
)로 사용할거다. models
의 User
에 avatarUrl
이 있다.
avatarUrl
이 바로 이 경로인건다.