본문 바로가기

개발/Node.js

[nodejs] Multer를 이용한 파일 업로드

파일 다루는 방식을 한번 정리해보고 싶어서 블로그를 작성해본다. 

파일 업로드는 일단 Multer라는 모듈을 사용할 것이다. Multer는 파일 업로드를 위해 사용되는 multipart/form-data 를 다루기 위한 node.js 의 미들웨어이다. 

 

Multer Reference를 참고해보자.

https://github.com/expressjs/multer/blob/master/doc/README-ko.md

 

GitHub - expressjs/multer: Node.js middleware for handling `multipart/form-data`.

Node.js middleware for handling `multipart/form-data`. - GitHub - expressjs/multer: Node.js middleware for handling `multipart/form-data`.

github.com

 

multer 설치

npm install --save multer

 

이제 multer를 사용 할 수 있다. 아주 간단한 예제를 만들어보기 앞서

views 하위 폴더에 upload.ejs와 confirmation.ejs 두가지를 생성할 것이다. 

upload.ejs는 파일 업로드할 form이 있는 ejs 이고 confirmation.ejs는 파일을 제출할 시 넘어가는 viewer이다.

 

 

저장할 폴더만 지정하여 저장하기

upload.ejs

<!DOCTYPE html>
<html>
  <head>
    <title>Multer 예제</title>
    <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css' />
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <div class="container">
      <h3>1.싱글파일, input text 업로드</h3><br/>
      <form class="row g-3" action="/uploadFile" enctype="multipart/form-data" method="post">
      <div class="mb-3 row">
        <label for="title" class="col-sm-2 col-form-label">제목</label>
        <div class="col-sm-10">
          <input type="text" class="form-control" name="title" >
        </div>
      </div>
      <div class="mb-3 row">
        <label for="content" class="col-sm-2 col-form-label">내용</label>
        <div class="col-sm-10">
          <input type="text" class="form-control" name="content">
        </div>
      </div>
      <div class="mb-3 row">
        <label for="file" class="col-sm-2 col-form-label">파일 첨부</label>
        <div class="col-sm-10">
          <input type="file" class="form-control" name="attachment">
        </div>
      </div>
      <div class="col-auto">
        <button type="submit" class="btn btn-primary mb-3">제출</button>
      </div>
      </form>
</body>
</html>

빠른 작성을 위해 bootstrap을 사용했다. 제목과 내용, 파일을 작성하는 form이다.

 

confirmation.ejs

<!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>
    <h1>Success!</h1>

    제목 : <%= title %>
    <br>
    내용 : <%= content %>
    
    <% if(file){ %>
    <pre><%=JSON.stringify(file, null, 2)%></pre>
    <% }%>
    
    <a href='/'>Back</a>
    </body>
</html>

폼 제출 성공시, 보여지는 화면이다. 제목과 내용, json 형태로 된 file의 정보를 보여준다.

 

routes/index.js

var express  = require('express');
var router   = express.Router();
var multer   = require('multer');
let upload = multer({ dest: 'uploadedFiles/' }); // 파일이 저장될 폴더 설정, 기본 multer

router.get('/', function(req,res){
  res.render('upload'); 
});

router.post('/uploadFile', upload.single('attachment'), function(req,res){ 

  res.render('confirmation', { file:req.file, title: req.body.title, content: req.body.content});
});

module.exports = router;

 

var multer = require('multer')를 이용하여 multer모듈을 사용한다는 것을 명시하고

upload라는 변수에 파일이 저장될 폴더만 지정해두었다. uploadFile에 저장하라고 명시해두었다.

 

결과값

이렇게 제목, 내용, 파일첨부를 선택하고 제출을 누르면

 

내용이 잘 전달됨을 볼 수 있다. uploadFile도 살펴보면

알수없는 이름의 파일이 저장되었다. 이 파일의 이름에 끝에 원래 확장자를 넣으면 첨부한 파일이 옳게 저장되었는지 확인해 볼 수 있다.

 

 

이렇듯, 파일 이름을 무작위로 생성되는게 아닌, 사용자가 지정하고 싶다면 multer.DiskStorage를 사용한다.

DiskStorage는 파일을 디스크에 저장하기 위한 모든 제어 기능을 제공한다. 

 

 


파일 이름 변경하여 저장하기

routes/index.js

var express  = require('express');
var router   = express.Router();
var multer   = require('multer');

var storage  = multer.diskStorage({
  destination(req, file, cb) { // 파일이 저장된 폴더	
    cb(null, 'uploadedFiles/');
  },
  filename(req, file, cb) { // destination 에 저장된 파일 명	
    cb(null, `${Date.now()}__${file.originalname}`);
  },
});

// let upload = multer({ dest: 'uploadedFiles/' }); 
let uploadWithOriginalFilename = multer({ storage: storage }); // 파일 명이 지정된 형식으로 변함 

router.get('/', function(req,res){
  res.render('upload'); 
});

router.post('/uploadFileWithOriginalFilename', uploadWithOriginalFilename.single('attachment'), function(req,res){
  res.render('confirmation', { file:req.file, files:null });
});

module.exports = router;

diskStorage를 통해 파일 이름을 현재 날짜_파일의 원래 이름으로 지정해주었다.

 

upload.ejs

<!DOCTYPE html>
<html>
  <head>
    <title>Multer 예제</title>
    <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css' />
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <div class="container">

      <h3>2. 싱글파일 업로드 (파일이름 유지))</h3><br/>
      <form action="/uploadFileWithOriginalFilename" enctype="multipart/form-data" method="post">
        <input type="file" name="attachment">
        <button type="submit" class="btn btn-primary">제출</button>
      </form>
      <hr>
      </div>
</body>
</html>

 

confirmation.ejs

<!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>
    <h1>Success!</h1>
    
    <% if(file){ %>
    <pre><%=JSON.stringify(file, null, 2)%></pre>
    <% }%>
    
    <a href='/'>Back</a>
    </body>
</html>

 

결과값

 

상훈이형.jpg 이라는 파일을 제출하였다.

 

filename에 1643000956034__상훈이형.jpg 라고 변경됨을 알 수 있다.

 

폴더에 지정한 파일 또한 잘 저장됨을 알 수 있다.

 


여러 파일 업로드

여러 파일을 업로드할때는 upload.single() 대신에 upload.array()를 사용한다.

주의할 점은 ejs에서 파일을 여러개 보낼 때는 <input type="file" multiple> multiple을 작성해 주는 것을 잊지 말자.

또한 파일을 하나만 보내면 req.file로 받으면 되는데 파일을 여러개 보내면 req.files로 받아야 한다. 

 

routes/index.js

var express  = require('express');
var router   = express.Router();
var multer   = require('multer');

var storage  = multer.diskStorage({
  destination(req, file, cb) { // 파일이 저장된 폴더	
    cb(null, 'uploadedFiles/');
  },
  filename(req, file, cb) { // destination 에 저장된 파일 명	
    cb(null, `${Date.now()}__${file.originalname}`);
  },
});
// enctype="multipart/form-data" 처음에는 텍스트만 하니까 안가졌는데 multer를 라우터 선언할 때 같이 선언하니까 됐다
let upload = multer({ dest: 'uploadedFiles/' }); // 파일이 저장될 폴더 설정, 기본 multer 세팅. 이름을 지정해주지 않아서 무작위로 변함
let uploadWithOriginalFilename = multer({ storage: storage }); // 파일 명이 지정된 형식으로 변함 

router.get('/', function(req,res){
  res.render('upload'); 
});

router.post('/uploadFiles', upload.array('attachments'), function(req,res){
  res.render('confirmation', { file: null, files:req.files});
});

router.post('/uploadFilesWithOriginalFilename', uploadWithOriginalFilename.array('attachments'), function(req,res){
  res.render('confirmation', { file:null, files:req.files });
});

module.exports = router;

파일 이름이 무작위로 바뀌는 것과 파일 이름을 지정할 수 있는 두가지로 구현을 해보았다.

 

 

upload.ejs

<!DOCTYPE html>
<html>
  <head>
    <title>Multer 예제</title>
    <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css' />
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body>
    <div class="container">
      <h3>3. 여러 파일 업로드</h3><br/>
      <form action="/uploadFiles" enctype="multipart/form-data" method="post">
        <input type="file" name="attachments" multiple>
        <button type="submit" class="btn btn-primary">제출</button>
      </form>
      <hr>

      <h3>4. 여러 파일 업로드 (파일이름 유지)</h3><br/>
      <form action="/uploadFilesWithOriginalFilename" enctype="multipart/form-data" method="post">
        <input type="file" name="attachments" multiple>
        <button type="submit" class="btn btn-primary">제출</button>
      </form>
    </div>
</body>
</html>

 

confirmation.ejs

<!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>
    <h1>Success!</h1>
    
    <% if(files){ %>
    <pre><%=JSON.stringify(files, null, 2)%></pre>
    <% }%>
    
    <a href='/'>Back</a>
    </body>
</html>

 

결과값

 

제발 그만해.jpg와 상훈이형.jpg를 선택한 뒤 제출을 클릭하면

 

두개 파일의 json array가 잘 출력됨을 볼 수 있고 

파일 업로드도 잘 됐음을 볼 수 있다.

 

다음시간에는 파일을 업로드하고 업로드한 파일의 섬네일을 보고 파일을 db에 저장하고 또 가져오는것 까지 해볼것이다. 

너무길면 나눠서 잘라서 구현해볼 것이다.

 

Reference

https://github.com/datoybi/blog-posting/tree/main/multer-file-storage

 

GitHub - datoybi/blog-posting: code examples posted on my blog

code examples posted on my blog. Contribute to datoybi/blog-posting development by creating an account on GitHub.

github.com