내용이 안 보인다면 쿠키/캐시를 지우고 새로고침 하세요!
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

게시글

강좌17 - NodeJS - 일 년 전 등록 / 6달 전 수정

multer를 사용해 이미지 업로드하기

폼데이터 처리
조회수:
0
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

안녕하세요. 이번 시간에는 multer를 사용해 이미지를 업로드 및 폼데이터를 처리하는 것을 알아보겠습니다.

보통 JSON 형식으로 된 데이터는 AJAX로든 폼 태그로든 쉽게 업로드할 수 있습니다. 하지만 이미지 파일만큼은 사람들의 골치를 썩이는데요. express와 함께 사용하면 쉽게 이미지 업로드를 도와주는 multer 모듈이 있습니다.

npm i multer

이제 설치된 multer를 사용해 코딩을 해봅시다. 익스프레스 라우터 부분에 다음을 추가해줍니다.

const multer = require('multer');
// 기타 express 코드
const upload = multer({ dest: 'uploads/', limits: { fileSize: 5 * 1024 * 1024 } });
app.post('/up', upload.single('img'), (req, res) => {
  console.log(req.file); 
});

이제 폼데이터나 폼 태그를 통해 업로드한 이미지를 올리면 req.file로 정보가 들어오고, dest 속성에 지정해둔 경로에 이미지가 저장됩니다. limits 속성은 선택 사항인데 여러 가지 제한을 걸 수 있습니다. 위에서는 파일 사이즈를 5MB로 제한했습니다. 폼데이터로 업로드하는 강좌는 여기를 참고하세요. upload.single('img') 미들웨어를 라우터 콜백함수 전에 끼워넣었는데요. 폼데이터의 속성명이 img이거나 폼 태그 인풋의 name이 img인 파일 하나를 받겠다는 뜻입니다. 이미지가 아닌 나머지 데이터는 그대로 req.body에 들어옵니다.

만약 이미지를 하나가 아닌 여러 개를 받고 싶다 하면 upload.array('키', 최대파일개수) 하면 됩니다. req.file 대신 req.files에 정보가 담깁니다.

app.post('/up', upload.array('img'), (req, res) => {
  console.log(req.files);
});

만약 여러 개의 키로 이미지를 올렸다면 upload.fields를 써야 합니다(이외에 upload.none도 있습니다). 사용한 키들을 배열 안에 넣어주면 됩니다.

app.post('/up', upload.fields([{ name: 'img' }, { name: 'photos' }]), (req, res) => {
  console.log(req.files);
});

문제는 uploads 폴더에 뭔가 생성이 되긴 하는데 이름도 ce243370b74107493fea0743d249a176처럼 이상하게 바뀌어있고 확장자도 붙어 있지 않아 쓸 수가 없습니다. 사실 이것은 보안상 의도된 것이지만 지금은 불편하기 때문에 이름이 원래대로 나오게 해보겠습니다. dest 속성 대신 storage 속성을 사용해 upload 변수에 넣어주면 됩니다.

const upload = multer({
  storage: multer.diskStorage({
    destination: function (req, file, cb) {
      cb(null, 'uploads/');
    },
    filename: function (req, file, cb) {
      cb(null, file.originalname);
    }
  }),
});

네. 조금 복잡해졌긴 하지만 위의 코드를 보시면 저장될 경로(destination)과 파일명(filename)을 조작하고 있음을 알 수 있습니다. 이제 다시 파일을 올리면 원본 파일명 그대로 올라갑니다. 그런데 파일명이 중복되는 경우 문제가 생길 수 있죠. 파일명을 타임스탬프로 해서 중복되지 않게 해봅시다.

const path = require('path');
const upload = multer({
  storage: multer.diskStorage({
    destination: function (req, file, cb) {
      cb(null, 'uploads/');
    },
    filename: function (req, file, cb) {
      cb(null, new Date().valueOf() + path.extname(file.originalname));
    }
  }),
});

위와 같이 하시면 타임스탬프.확장자 형식으로 파일명이 지정됩니다.

지금까지는 실제 디스크에 파일을 업로드했는데요. S3같은 곳에 업로드하시는 분들도 있을 겁니다. S3에 업로드하는 방식은 크게 두 가지가 있는데요. 디스크에 있는 파일을 업로드하거나, 파일 버퍼(메모리에 저장)를 업로드하는 겁니다. 파일을 S3에 업로드한 후에는 남아있는 파일을 지워줘야 하는데 이게 번거롭죠. 그래서 처음부터 메모리에 파일을 버퍼 형식으로 저장하고, 그것을 업로드하는 겁니다.

const upload = multer({
  storage: multer.memoryStorage(),
});

메모리스토리지를 쓸 경우는 req.file이나 req.files 안의 파일 데이터(객체)에, 디스크스토리지 전용 속성인 destination, filename, path 대신 buffer라는 속성이 새로 생기고 그 값으로 버퍼들이 저장됩니다. 이 버퍼를 사용해서 S3에 버퍼로 업로드하시면 됩니다(또는 multer-s3 패키지를 사용하면 쉽게 가능합니다). 이 방식의 단점은 파일이 여러 개고, 용량이 너무 크면 메모리를 초과해서 서버가 멈춰버릴 수도 있습니다. 보통은 그럴 일은 없겠지만 그럴 수도 있다는 것을 알아두세요.

투표로 게시글에 관해 피드백을 해주시면 많은 도움이 됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright © 2016- 무단 전재 및 재배포 금지

댓글

14개의 댓글이 있습니다.
18일 전
제가 잘 안되는게 있어서 그러는데 혹시 괜찮으시다면 도움을 좀 받을 수 있을까요?안되는 이유라도 알고 싶은데 그걸 모르겠어서....
18일 전
아 그러네요...감사합니다!
18일 전
그러면 제가 만약 photoF라는 폴더에 사진을 저장하고 싶을경우에는
fs.writeFile(new Date().toLocaleString()+'./photoF/photo.jpg',base64Decode,function(err)
이렇게 하면 되나요??ㅜ
18일 전
new Date().toLocaleString()은 왜 자꾸 붙이시는거죠? 파일 이름에 붙이셔야 하지 않을까요?
18일 전
파일의 경로라는게 그 파일이 저장될 위치를 말하시는건가요?
18일 전
18일 전
오!! 감사합니다.그리고 예를 들어
base64Decode = new Buffer.from(urldoce,'base64');후에
fs.writeFile(new date().toLocaleString(),base64Decode,function(err) 이런식으로 하면 폴더에 jpg파일로 저장 되나요?ㅠㅠ 제가 입문자라 숟가락을 떠주셔서 먹질 못해서..
18일 전
writeFile의 첫 번째 인자는 파일경로니까 파일 경로를 제대로 지정해주셔야겠죠. jpg 확장자도 붙이고요.
19일 전
궁금한게 있는데요 저는 이미지를 base64->url로 바꿔서 받은 후 노드 서버에서 url->base64로 디코딩을 해줬는데요 그렇게 해서 비트맵으로 바꿔서 사용할려고 하는데 안드로이드에서 보낸것처럼 node에서 bitmapFactory같으게 사용할 수 있나요? 없으면 어떻게 다시 bitmap으로 바꿀지가 막막해서.ㅠ
19일 전
base64문자열을 Buffer.from(문자열, 'base64') 하시면 바이너리로 변경됩니다. 그걸로 fs.writeFile 하시거나 하시면 됩니다.
한 달 전
multer를 사용하면 form-data를 req.body에 예쁘게 (body-parser처럼) 넣을수 있는데 문제는 제가 값체크를 먼저하고 (권한체크등) 나중에 업로드를 하고싶습니다. 그럴려면 bodyparser가 동작해야하는데 문제는 form-data에 경우에는 먹히질 않네요 어떻게 할수있을까요?...
한 달 전
fileFilter라는 옵션이 있습니다.
한 달 전
파일필터 옵션은 제가 보기에 파일의 값을 체크하는것 같은데 제가 말한 값 체크는 예를들어서 name값이 10자를 넘는지, content가 10자 이상인지등의 값 체크입니다 보통은 그냥 body.name... 으로 호출해서 쓰면 되는데 form-data 는 body-parser가 안먹어서요... 혹시 멀티파트 데이터를 parser하는 기능을 아시나요?... 한마디로 파일업로드전에 body 값을 체크하고싶습니다
한 달 전
fileFilter에서 req에 접근할 수 있는데 req.body로 접근이 안 되시나요? formData에 키를 추가하는 순서도 중요합니다(이미지를 제일 마지막에 추가해야 합니다)
한 달 전
책의 예제를 그대로 적었는데
POST /profile/upload 413 11.326 ms - 2641
PayloadTooLargeError: request entity too large 라는 에러가 발생했습니다.
구글링을 했고 여러시도를 해봤습니다. limits: { fileSize: 5 * 1024 * 1024 }을 limits: {fileSize: 100000000}로 바꾸거나app.use(bodyParser.json({limit:'50mb'}));
app.use(bodyParser.urlencoded ({extended: true,limit:'50mb',parameterLimit:50000 }));을 app.js에 추가해보았습니다
postman의 form-data에 400kb짜리 사진을 업로드 해봤지만.... POST /profile/upload 413 13.445 ms - 2641
PayloadTooLargeError: request entity too large 여전히 이렇네요
한 달 전
에러 메시지만 보면 용량제한에 걸린 것 맞는데요. 포스트맨 말고 그냥 웹으로 한 번 보내보시겠어요?
2달 전
대댓글로 썼는데 댓글이 날라갔네요ㅠ

기존에 <form>태그로 넘기면 uplaod.single()에 file input의 id를 넘기잖아요,
근데 formdata로 넘기면 여기에 넣을게 없는데 이걸 어떻게 해결해야 하나요?

지금 클라이어늩에서 서버로 req.files.image에 버퍼 이런게 넘어오기만하고

이 넘어온 데이터를 가지고 실제로 uploads/ 폴더에 올리는 방법을 모르겠습니다ㅠㅠ
2달 전
formData.append('image', 이미지데이터)하시면 upload.single('image')를 통해 req.file.image가 되는 식입니다. 이렇게 하면 uploads 폴더에도 올라갑니다.
2달 전
https://gist.github.com/YanghaKoo/0743a6d70f4027a3cd7ceffde6768863

말씀하신대로 해봤는데 req.file이 계속 undefined이고, 업로드가 되지 않습니다..ㅠ
혹시 괜찮으시다면 한번만 봐주실수 있나요..?
2달 전
안녕하세요 앞 강의인 formData로 해서 파일을 넘겼습니다

req.files.image = {
name: '이미지.jpg',
data: <Buffer ff d8 ff e1 28 1b 45 78 69 66 00 00 49 49 2a 00 08 00 00 00 0c 00 0e 01 02 00 20 00 00 00 9e 00 00 00 0f 01 02 00 05 00
00 00 be 00 00 00 10 01 02 00 ... >,
encoding: '7bit',
truncated: false,
mimetype: 'image/jpeg',
md5: [Function: md5],
mv: [Function: mv]
}


이걸 multer로 업로드 하려면 어떻게해야하나요??
multer쪽의 코드는 제로초님 책을 보고 작성했습니다
2달 전
req.files.image라는 건 이미 멀터로 업로드한 결과물 아닌가요?
6달 전
폼데이터를 업로드하는 방법의 링크가 여기 링크와 똑같이 나와요!
6달 전
수정했습니다 감사합니다
8달 전
게시판과 같은 것을 만들 때는 파일을 업로드하지 않고, 어떤 방법을 쓰나요? 데이터베이스를 쓰나요, 아니면 그냥 파일을 쓰나요?
8달 전
디비보다는 파일을 씁니다. 하지만 파일을 서버에 저장하기 보다는 cdn에 저장하곤 합니다.
일 년 전
안녕하세요^^ 혹시 파일업로드 관련해서 혹시 도움 좀 주실 수 있을까요?
일 년 전
multer 사용하시나요?
일 년 전
그건 아닌데 폼 방식이 multipart/form-data 여서요~
일 년 전
어떤 부분이 궁금하신가요??
일 년 전
내용이 조금 있어서 혹시 메신저나 이메일 주소 등의 연락방법이 있을가요^^?
일 년 전
옆의 페북 메신저 사용하시거나 알림 메일가는 주소로 연락주시면 됩니다.
일 년 전
안녕하세요. 글 잘 읽어봤는데요. 한가지 궁금한 점이 있는데 이 게시글에서는 multer 모듈을 사용해서 이미지를 파일로 저장하셨는데 찾아보니까 몽고 디비를 이용해서 저장을 하는것도 있더라구요. (https://medium.com/@alvenw/how-to-store-images-to-mongodb-with-node-js-fb3905c37e6d) 속도나 효율 등의 면에서 보통 어떤 방식이 주로 사용되는지 알 수 있을까요? 그리고 'S3같은 곳에 업로드하시는 분들도 있을 겁니다' 에서 S3가 뭔가요?
일 년 전
S3는 아마존의 파일 스토리지입니다. 파일로 저장하는 것이 몽고디비 gridfs 등을 사용하는 것보다 편리합니다.