NodeJS+MongoDB

NodeJS+MongoDB Part3 - 이미지 업로드 & 이미지 서버 만들기

hminor 2023. 6. 28. 17:03

이미지 업로드 & 이미지 서버 만들기

upload.ejs를 만들어서 form 태그안에 input 타입으로 file을 선택해서 파일 전달할 수 있도록 하기

input 속성의 enctype은 아래의 주석을 보고 이해하기.

<body>
  <%- include('nav.html')%>

  <div class="container mt-3">
    <h4 class="mt-4"><strong>업로드 페이지</strong></h4>
    <!-- enctype 속성은 인코딩 타입을 의미하는 건데
      application/x-www-form-urlencoded 는 URL을 인코딩해서 보내겠다는 것. (Default) <-- base64 인코딩 형식으로 인코딩 됨.
      multipart/form-data 는 인코딩하지말고 파일 전송해달라는 것.
    -->
    <form action="/upload" method="POST" enctype="multipart/form-data">
      <input type="file">
    </form>
  </div>
</body>

server.js 에도 만들 upload로 방문했을때 upload.ejs가 보일 수 있도록 해주기.

app.get("/upload", (req, res) => {
  res.render("upload.ejs");
});

여기서 잠시 이제 업로드한 파일을 어디서 보관하는지에 대해 생각해본다면

db에 그냥 저장하는게 좋지 않을까? 라고 생각하지만

보통 저렴하게 하드디스크에 저장한다고 한다, 아니면 클라우드 서비스의 하드디스크를 빌려서 사용하던지!

그래서 우선 프로젝트 내에 폴더를 만들어 저장하는 방식을 해본다면

라이브러리를 설치해서 더욱 쉽게 구현하도록 하기

  • 해당 라이브러리는 multipart Data를 쉽게 처리할 수 있도록 해주는 것.
  • 즉 파일 전송을 쉽게 구현하면서, 파일명을 분석해주는 것.
npm i multer

이후 아래처럼 코드를 작성하기

// multer 사용법
const multer = require("multer");
// 이미지를 어디에 저장할 것인지 인데
//  diskStorage는 폴더 안에 저장해달라는 것.
//  memoryStorage는 렘에 저장해달라는 것(휘발성).
const storage = multer.diskStorage({
  destination: (req, file, cd) => {
    cd(null, "./public/images"); // 저장할 파일 경로
  },
  filename: (req, file, cd) => {
    // 저장한 파일의 파일명 설정 --> originalname은 파일명 그대로
    cd(null, file.originalname);
  },
});

const path = require("path");

// 필터랑 제한을 주고 싶을 땐 multer({}) 안에 작성해야한다.
const upload = multer({
  storage: storage,
  // 파일 필터 걸기
  fileFilter: function (req, file, callback) {
    var ext = path.extname(file.originalname);
    if (ext !== ".png" && ext !== ".jpg" && ext !== ".jpeg") {
      return callback(new Error("PNG, JPG만 업로드하세요"));
    }
    callback(null, true);
  },
  limits: {
    fileSize: 1024 * 1024,
  },
});

app.get("/upload", (req, res) => {
  res.render("upload.ejs");
});

// 미들웨어처럼 upload 사용하기.
// app.post('/upload', single('input태그의 name속성'), (req, res)=> {
//    만약 파일 여러개를 업로드하고 싶다면 upload.single이 아니라 upload.array('name속성', 최대 개수)로 변경해주고
//    upload 해주는 input도 여러개로 넣을 수 있게 multiple 속성을 추가로 해줘야함. --> 이게 그냥 클릭하면 안되고 컨트롤 누르고 해야 적용됨.
app.post("/upload", upload.array("upload", 2), (req, res) => {
  res.redirect("/");
});

// 업로드한 이미지 보여주기.

app.get("/images/:fileName", (req, res) => {
  // __dirname 는 현재 파일의 경로를 의미
  // res.sendFile( __dirname + 'public/images' + 이미지이름)
  res.sendFile(__dirname + "/public/images/" + req.params.fileName);
});
<!-- upload.ejs -->
<!-- 선택한 파일을 미리보기도 할 수 있게 했음. (물론 여러개 선택해도 하나만 보이지만) -->

<!doctype html>
<html>

<head>
  <!-- Required meta tags -->
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css"
    integrity="sha384-xOolHFLEh07PJGoPkLv1IbcEPTNtaed2xpHsD9ESMhqIYd0nLMwNLD69Npy4HI+N" crossorigin="anonymous">
  <link rel="stylesheet" href="/public/main.css">

  <title>NodeJS+MongoDB</title>
</head>

<body>
  <%- include('nav.html')%>

  <div class="container mt-3">
    <h4 class="mt-4"><strong>업로드 페이지</strong></h4>
    <!-- enctype 속성은 인코딩 타입을 의미하는 건데
      application/x-www-form-urlencoded 는 URL을 인코딩해서 보내겠다는 것. (Default) <-- base64 인코딩 형식으로 인코딩 됨.
      multipart/form-data 는 인코딩하지말고 파일 전송해달라는 것.
    -->
    <form action="/upload" method="POST" enctype="multipart/form-data">
      <input type="file" name="upload" id="file" multiple>
      <button type="submit">업로드</button>
    </form>
    <img id="preview" src="" alt="">
  </div>



  <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"
    integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous">
  </script>
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/js/bootstrap.bundle.min.js"
    integrity="sha384-Fy6S3B9q64WdZWQUiU+q4/2Lc9npb8tCaSX9FK7E8HnRr0Jz8D6OP9dO5Vg3Q9ct" crossorigin="anonymous">
  </script>

  <script>
    $(function () {
      $("#file").on('change', function () {
        readURL(this); // 선택한 파일을 읽고, 해당 파일의 데이터 URL을 생성 후 파일을 처리
      });
    });

    function readURL(input) {
      if (input.files && input.files[0]) {
        var reader = new FileReader();
        reader.onload = function (e) {
          $('#preview').attr('src', e.target.result);
        }
        reader.readAsDataURL(input.files[0]);
      }
    }
  </script>

</body>

</html>