파일을 업로드 하는 방법에 대해 알아보자.
우선 하나의 파일을 올리는 것부터 시작해서
비동기적으로 업로드를 진행하는 방법, 파일 여러 개를 올리는 방법을 알아볼 것이다.
0. 배경지식
우선 알고 시작하면 좋은게 있다.
파일의 경로, 확장자 등에 대한 처리를 쉽게 해주는 방법인데, nodejs의 내장 라이브러리 중 path가 있다.
파일을 업로드 하기위해서는 우선 그 파일을 어디 올릴건지, 확장자가 뭔지, 파일명은 뭔지 등을 다 컴퓨터에
인식시켜줄 필요가 있고, 이 인식을 쉽게하도록 도와주는게 path 라이브러리이다.
몇 가지 사용 예시를 알아보자.
path.dirname(filePath) : 디렉토리 추출 후 출력
console.log(path.dirname('/home/sila/work/sila.js'))
// home/sila/work
--------------------------
path.basename(filepath) : 파일명 추출 후 출력
console.log(path.basename('/home/sila/work/sila.js')
// sila.js
--------------------------
path.extname(filepath) : 파일 확장자 추출 후 출력
console.log(path.extname('server.js'))
// .js
--------------------------
path.basename(filepath, 확장자) : 확장자 제외한 파일명 출력
console.log(path.basename('home/sila/work/server.js', '.js'))
// server
1. intro
우선 다음과 같이 js와 html을 만들어주자.
/* server.js */
const express = require('express')
const app = express()
const nunjucks = require('nunjucks')
app.set('view engine', 'html')
nunjucks.configure('views', {express:app})
app.use(express.json())
app.use(urlencoded({extended:true}))
app.get('/single', (req, res) => {
res.render('single.html')
})
app.listen(3000, () => {
console.log('server run 3000')
})
/* single.html */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form method="post" action="/upload" enctype="multipart/form-data">
<input type="text" name="subject">
<input type="file" name="upload">
<input type="submit" value="전송">
</form>
</body>
</html>
html을 잘 보면 두 번째 input element의 type이 'file'인 것, form element에 enctype attribute가 있음을 알 수 있다.
enctype의 value를 multipart/form-data 로 지정해주어야 이미지, 파일을 서버로 전송할 수 있다.
input element의 type은 말 그대로 file이다.
잘 되는지 확인을 하고 js로 돌아온다.
2. 파일 업로드를 위한 set up
2.1 multer
서버로의 업로드를 위해서는 multer라는 외부 라이브러리와 path라는 node 내장 라이브러리가 필요하다.
multer는 사용자가 전송한 파일을 처리하는 작업을 도와준다.
multer안에 있는 객체 upload를 가져와 업로드 과정에 대한 format을 지정하고
그걸 미들웨어로서 라우터 안에 넣어주는 방식이다.
path는 파일의 경로를 지정하는 것을 편하게 해주는 내장 라이브러리이다.
npm을 이용해 multer를 설치해주고 다음과 같이 js에 코드를 추가해준다.
/* server.js */
const multer = require('multer')
const path = require('path')
이제 파일 업로드의 포맷과 요청을 보내는 uri의 미들웨어에 대한 처리가 필요하다.
차례차례 해보자.
/* server.js */
const upload = multer({
storage:multer.diskStorage({
destination:(req, file, done) => {
done(null, 'uploads')
},
filename:(req, file, done) => {
const ext = path.extname(file.originalname)
const fielname = path.basename(file.originalname, ext) + '_' + Date.now() + ext
done(null, filename)
}
}),
limits: { file : 5 * 1024 * 1024 }
})
destination의 cb의 세 번째 인자값 done의 두 번째 인자값 'uploads' 가 파일이 저장될 위치를 의미한다.
우리는 uploads 폴더에 업로드된 파일을 저장할 것이다.
(여기까지 적었으면 uploads 폴더를 server.js와 동일한 디렉토리에 만들어주자.)
filename은 저장할 파일의 이름을 정하는 것으로, 아까 다룬 path를 이용해 쉽게 파일명을 지정해줄 수 있다.
여기서는 파일의 확장자와 파일의 이름을 따로 추출한 후, 사이에 업로드된 날짜를 넣어줌으로써
혹시라도 이름이 같은 파일이 다시 올라왔을 때 발생할 수 있는 문제를 방지해주었다.
limits는 업로드 할 수 있는 최대 용량을 의미한다. 기본 단위는 byte로 여기서는 5MB를 최대치로 설정했다.
2.2 라우터
이제 form이 제출될 때 요청을 보내는 uri의 라우터를 만들어주자.
/* server.js */
app.post('/upload', upload.single('upload'), (req, res) => {
res.send('upload successful')
})
html에서 form의 action attribute의 value인 '/action'에 대응하는 라우터를 만들어야 한다.
form element의 method가 post 였으므로 라우터도 post를 사용한다.
그 후, 아까 선언해준 변수 upload에 single method를 연결해준다.
(single 은 하나의 파일을 전송할 때 사용하는 method이고 여러 개를 전송할 때는 다른 method 사용)
괄호 안의 인자값에는 업로드할 파일을 담은 html element의 name을 넣어준다.
/single 페이지에서 업로드가 문제없이 된다면 /upload 페이지로 이동하고
'upload successful' 이라는 문구를 볼 수 있을 것이다.
3. 비동기적 파일 업로드
페이지를 바꾸거나 할 필요없이 비동기적으로 파일 업로드를 진행해보자.
저번 포스트에서 한 것처럼 axios를 사용하면 된다.
우선 라우터와 html을 추가해주자.
/* server.js */
app.get('/axios', (req, res) => {
res.render('axios.html')
})
/* axios.html */
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>
<body>
<form method="post" action="/upload" enctype="multipart/form-data">
<input type="text" name="subject">
<input type="file" name="upload2">
<input type="submit" value="전송">
</form>
</body>
</html>
head 부분에 axios 사용을 위한 script 한줄을 추가하고, 구분을 위해 file input element의 name을 바꾼 것 이외에는
single.html과 동일하다.
이제 body 부분에 script를 추가해 비동기적인 파일 업로드를 구현해볼 것이다.
/* axios.html */
<script type='text/javascript'>
const frm = document.querySelector('form')
frm.addEventListener('submit', async (e) => {
e.preventDefault()
const { subject, upload2 } = e.target // e.target == <form> ... 중략... </form>
const formData = new FormData()
formData.append('upload2', upload2.files[0])
formData.append('subject', subject.value)
const response = await axios.post('upload2', formData)
</script>
요청을 보낼 내용인 form element 전체를 변수에 담은 후,
submit 시 발동하는 cb를 만든다.
원래의 submit 기능을 preventDefault()를 이용해 실행되지 않게 막고
form element 중 subject와 upload2 만 가져온다. (name 값으로 선택해 가져온다.)
이 둘을 formData에 추가해주고 (FormData는 내장 객체이므로 new와 함께 써준다.)
지정한 uri로 보내주게끔 axios를 이용해 전송한다.
이제 server.js에서 이를 받을 라우터를 만들어야한다.
/* server.js */
app.post('/upload2', upload.single('upload2'), (req, res) => {
res.send('upload successful2')
}
uri와 name만 제외하면 처음에 한 동기적 업로드와 동일하다.
3. 여러 개 파일 한 번에 올리기
다음과 같이 js와 html을 작성해준다.
/* server.js */
app.get('/array', (req, res) => {
res.render('array.html')
}
/* array.html */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form method="post" action="/upload3" enctype="multipart/form-data">
<input type="text" name="subject">
<input type="file" name="upload3" multiple>
<input type="submit" value="전송">
</form>
</body>
</html>
js쪽은 특별한 것이 없고,
file input element의 name, 추가적인 attribute으로 multiple이 있는 것에 주의한다.
multiple은 복수의 파일을 업로드 하고 싶을 때 사용해주면 된다.
다시 server.js로 돌아가 제출한 파일을 받아줄 라우터를 추가하자.
/* server.js */
app.post('/upload3', upload.array('upload3'), (req, res) => {
res.send('upload successful3')
})
여기까지 한 후, 브라우저로 웹 페이지에 접속해서 파일을 한 번에 여러 개 업로드 해보자.
3. 정해진 갯수만의 파일을 올리기
마지막으로 필요한 갯수만큼만을 업로드 할 수 있게 하는 코드를 작성해보자.
/* server.js */
app.get('/uploads', (req, res) => {
res.render('uploads.html')
})
/* uploads.html */
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form method="post" action="/upload4" enctype="multipart/form-data">
<input type="text" name="subject">
<input type="file" name="upload1">
<input type="file" name="upload2">
<input type="file" name="upload3">
<input type="file" name="upload4">
<input type="submit" value="전송">
</form>
</body>
</html>
file input을 원하는 만큼 늘리고 각각 이름을 정해준다. (여기선 4개)
이 후 이를 받아줄 라우터를 동일한 방법으로 method만 바꿔서 만들어주면 된다.
/* server.js */
app.post('/upload4', upload.fields([ { name: 'upload1'}, { name : 'upload2'},
{ name: 'upload3' }, { name: 'upload4' } ]), (req, res) => {
res.send('upload successful4')
})
upload에 사용할 method는 fields 이며, 각각의 element를 전부 { name : '값' } 객체 형태를 원소로 갖는
배열로 인자값을 지정해주어야 한다.
이렇게 하고 웹 페이지로 들어가보면 파일을 업로드하는 input element가 4개 있을 것이다.
파일을 선택하고 submit 했을 때, uploads 폴더 안에 파일들이 추가되면 성공이다.
'Nodejs > Server' 카테고리의 다른 글
게시판 서버 만들기 #2 - 로그인 (0) | 2022.03.19 |
---|---|
게시판 서버 만들기 #1 - 회원가입 (0) | 2022.03.19 |
비동기 통신 - AJAX #1 (0) | 2022.03.16 |
Session #2 - 미들웨어 (0) | 2022.02.13 |
Session #1 (0) | 2022.02.13 |