2023.05.30 이미지 업로드(multer)

이무헌·2023년 7월 21일
0

node.JS

목록 보기
9/10
post-thumbnail

1.multer로 이미지 mysql에 업로드 하기

1.json 바디 받기

app.use(e.json());
app.use(e.urlencoded({ extended: false }));

2.image 를 처리하는 middleware

const multer = require("multer");
const path=require('path')
const storage = multer.diskStorage({
  destination: (req, file, cb) => {
    cb(null, "20230530_2/imgs"); // Set the destination folder where the uploaded files will be stored
  },
  filename: (req, file, cb) => {
    const ext = path.extname(file.originalname);

    const filename =
      path.basename(file.originalname, ext) + "_" + Date.now() + ext;
    cb(null, filename); // Use the original filename for the stored file
  },
  limits: { fileSize: 5 * 1024 * 1024 }, //5MB
});

const upload = multer({ storage });

module.exports = upload;
  • multer.diskStorage는 하드디스크에 이미지를 저장할 때 설정하는 매개변수이다.
  • destination 은 저장될 목적지(경로)를 뜻 한다.
  • 현재 root폴더부터 시작되므로 root안에 있는20230530_2안의imgs폴더에 모든 업로드 이미지를 저장한다.
  • filename: (req, file, cb) 로 이미지의 이름을 재정의한다.
  • const ext = path.extname(file.originalname); 로 파일의 확장자명을 따로 분리 하여 할당할 수 있다.
  • path.basename(file.originalname, ext) 로 파일의 확장명이 없는 파일 이름만 분리하여 할당 할 수 있다.
  • const filename =
    path.basename(file.originalname, ext) + "_" + Date.now() + ext; 로 현재 파일의 이름+현재날짜+확장자명 으로 파일 이름을 정한다.
  • cb(null, filename) 로 해당 파일이름을 변경한다.
  • limits: { fileSize: 5 1024 1024 }, 로 파일의 최대용량을 설정한다.
  • const upload = multer({ storage });

module.exports = upload;로 내보내자

3.signUp

exports.signUp = async (req, res, next) => {
  try {
    const { filename } = req.file;
    const { user_id, user_pw, username } = req.body;
    const data = await User.findOne({ where: { user_id } });
    if (data == null) {
      const hash = bcrypt.hashSync(user_pw, 10);
      await User.create({
        user_id,
        user_pw: hash,
        username,
        img: filename,
      });
      res.send("성공");
      console.log("req.file");
    } else {
      res.send("중복됨");
    }
  } catch (error) {
    console.log(error);
  }
};
  • const { filename } = req.file;로 filename을 받는다.
  • filename을 받는 이유는 파일의 이름을 mysql에 저장하여 view에 보여줄 때 파일 이름으로 src를 찾기 위함이다.
  • 어차피 경로는 미들웨어로 고정시킬 것이다.
    app.use("/img", e.static(path.join(__dirname, "imgs"))); 
    • 미들웨어로 img로 들어오는 모든 경로는 imgs안의 있는 리소스에 접근하도록 만들었다.

3.router에서 받기

const upload = require("../uploadImg/uploadImg");
const router = require("express").Router();
router.post("/", upload.single("profile"), signUp);
  • 갖고온 upload에서 single메서드로 매개변수에 html에서 보낸 input의 name을 넣는다.
  • 중요한건, 미들웨어의 순서가 바뀌면 안된다! 즉 singUp이 앞에 있으면 안된다.
  • req.body와 file에 값을 할당시켜주는 미들웨어가 먼저 있어쟈이 body와 file로 받을수 있다.

4.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>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
  </head>
  <body>
    <label for="">name</label>
    <input type="text" id="username" />
    <label for="">id</label>
    <input type="text" id="user_id" />
    <label for="">password</label>
    <input type="password" id="user_pw" />
    <label for="">profile image</label>
    <input type="file" id="img" />
    <button id="signUpBtn">signUp</button>
  </body>
  <script>
    signUpBtn.onclick = () => {
      const form = new FormData();
      form.append("user_id", user_id.value);
      form.append("user_pw", user_pw.value);
      form.append("username", username.value);
      form.append("profile", img.files[0]);

      console.log(user_id.value);
      axios
        .post("http://127.0.0.1:8080/signUp", form, {
          "Content-Type": "multipart/form-data",
        })
        .then((e) => {
          window.location.href =
            "http://127.0.0.1:5500/20230530_2/login.html";
          console.log(e, "성공");
        })
        .catch((err) => {
          console.log(err);
        });
    };
  </script>
</html>
  • 다음 코드를 보자
     const form = new FormData();
          form.append("user_id", user_id.value);
          form.append("user_pw", user_pw.value);
          form.append("username", username.value);
          form.append("profile", img.files[0]);
    • form이라는 새로운 js객체를 선언해서 append로 값을 할당한다.

    • 첫 번째 매개변수는 서버에서 받을 이름(즉,input의 name이다.)을, 두 번째 매개변수로는 전달해줄 값을 입력한다.

    • img는 files[0]이 그 파일의 정보이다.

       axios.post("http://127.0.0.1:8080/signUp", form, {
                "Content-Type": "multipart/form-data",
              })
    • axios로 데이터를 보낸다."Content-Type": "multipart/form-data",로 헤더를 꼭 설정해주자

      then((e) => {
                window.location.href =
                  "http://127.0.0.1:5500/20230530_2/login.html";
                console.log(e, "성공");
              })
    • axios는 절대로 서버단에서 res.redirect와 같은 http요청을 받을 수 없다!

    • 그러므로 클라이언트단에서 이동할 곳을 처리하자

      2.view로 가져오기

      <!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>
          <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
        </head>
        <body>
          ㅎㅇ
          <h1 id="name"></h1>
          <h1 id="user_id"></h1>
          <img src="" alt="profile" id="user_img" />
          <a href="http://127.0.0.1:8080/signUp/changeUserProfile">프로필 변경하기</a>
        </body>
        <script>
          window.onload = () => {
            axios
              .get("http://127.0.0.1:8080/signUp/getAllUserInfo", {
                withCredentials: true,
              })
              .then((e) => {
                const { data } = e;
                name.innerText = data.username;
                user_id.innerText = data.user_id;
                user_img.src=`http://127.0.0.1:8080/img/${data.img}`
                console.log(e);
              })
              .catch((err) => {
                console.log(e);
              });
          };
        </script>
      </html>
    • 위와 같이 파일의 이름만을 Mysql에 저장했으니 백틱으로 이미지의 이름만을 변수로 넣어줘서 화면에 표시해주면 된다.

      스크린샷 2023-05-30 오후 4.04.13.png

      3.느낀점

      💡 이미지를 no-sql에서 밖에 다루지 않아서 관계형 데이터베이스에서는 어떻게 다뤄야 할지 막막 했지만 생각보다 단순했다. 서버단에 이미지를 저장하고 경로를 이용해 화면에 표시해주면 되는것이다. 오히려 경로와 header를 처리하는 부분이 어려웠다. 특히 content-type을 처리하는 부분을 주의해야한다. json을 처리하는 것이 아닌, 이미지와 body를 동시에 처리해야 하므로 미들웨어를 적절히 사용해줘야 한다. 앞으로 헤더와 미들웨어 같은 데이터를 다루는 여러 방법을 더 공부해야겠다.
profile
개발당시에 직면한 이슈를 정리하는 곳

1개의 댓글

comment-user-thumbnail
2023년 7월 21일

정말 좋은 글 감사합니다!

답글 달기