본문 바로가기
개발기록

NodeJS(Express사용)에서 multer files upload 이미지 여러개 올리기 (+미리보기 포함)

by 떤떤 2021. 1. 15.

multer를 이용해서 사진 여러개 올리기 정말 힘드네요.

코드 수정이랑 새로고침을 몇 번이나 했던지 ㅠ

 

single파일 올리는 건 쉬워서 여러개 올리는 것도 별 거 아닌줄 알았는데 끙

다음에 다른 프로젝트 할 때 이때를 회상하며 쉽게 일하라고 올립니다.

아직 초심자의 단계라서 어렵게 돌아가는 걸 수도 있어요. 더 쉬운 방법이 있다면 댓글로 알려주시면 감사하겠습니다.

 

사용되는 파일

1. main.ejs (Client)

2. twit.js (Client)

3. post.js (Server)

 

 

1. html : main.ejs

<form
            id="twit-form"
            action="/post"
            method="post"
            enctype="multipart/form-data"
            name="twit-form"
          >
            <div class="form-group">
              <textarea
                id="twit"
                name="content"
                maxlength="140"
                class="form-control"
              ></textarea>
            </div>
            <div class="img-preview"></div>
            <div>
              <label id="img-label" for="img">사진 업로드</label>
              <input
                id="img"
                name="img"
                type="file"
                accept="image/*"
                multiple
              />
              <button id="twit-btn" type="submit" class="btn">업로드</button>
            </div>
</form>

- multer를 사용하면 form에 꼭 enctype="multipart/form-data"를 써줘야합니다.

- file 다중업로드를 하기 위해선 input type="file" 옆에 꼭 multiple 넣어주세요. 

- <div class="img-preview"></div> 여기에 미리보기 사진 동적으로 추가할 겁니다.

 

 

2. twit.js (클라이언트 쪽 javascript입니다. main.ejs에서 <sciprt></script> 사이에 쓰고 저장가능)

if (document.getElementById("img")) {
  let uploadNum = 0; //올린 사진 셀 변수
  let index = 0; //img 에 붙일 index
  document.getElementById("img").addEventListener("change", function (e) {
    const formData = new FormData(); //서버로 보낼 이미지 form
    const length = this.files.length;
    const max = 4; //사진 최대 4장까지
    switch (uploadNum) {
      case 0:
        if (length > max - uploadNum) {
          alert("사진은 최대 4장까지만 가능합니다.");
          return;
        }
        uploadNum += length;
        break;
      case 1:
        if (length > max - uploadNum) {
          alert("사진은 최대 4장까지만 가능합니다.");
          return;
        }
        uploadNum += length;
        break;
      case 2:
        if (length > max - uploadNum) {
          alert("사진은 최대 4장까지만 가능합니다.");
          return;
        }
        uploadNum += length;
        break;
      case 3:
        if (length > max - uploadNum) {
          alert("사진은 최대 4장까지만 가능합니다.");
          return;
        }
        uploadNum += length;
        break;
      default:
        alert("사진은 최대 4장까지만 가능합니다.");
        return;
    }
    console.log("업로드한 사진 : ", uploadNum);
    console.log("현재 올린 사진 : ", this.files);
    for (let i = 0; i < length; i++) {
      formData.append("img", this.files[i]);
      index++;
    }
    axios.post("/post/img", formData).then((res) => {
      let url = JSON.parse(res.data);
      console.log(url);
      let img_html = "";
      for (let i = 0; i < url.length; i++) {
        console.log("미리보기", url[i]);
        img_html += `<div class="img-preview${index}">
                  <img id="img-preview${index}" src="${url[i]}" width="250" alt="미리보기">
                  <input id="img-url" type="hidden" name="url" value="${url[i]}">
                  </div>`;
        console.log("json 길이 : ", url.length);
        console.log("서버통신index:", index);
        console.log(img_html);
      }
      $(".img-preview").append(img_html);
    });
  });
}

- 우선 img가 있는지 확인합니다. 올릴 수 있는 최대 파일 개수는 4로 정했습니다. switch문으로 4장 넘는지 확인하고

  넘지 않으면 올린 파일 개수 더해줍니다.

- id가 "img"인 input file을 선택하면("change"이벤트발생) formData에 넣습니다.

- formData를 ajax로 서버에 넘깁니다. 통신이 잘 됐다면 json파일을 받은 후 parsing합니다.

- 빈 html 변수에 json으로 받은 파일 경로를 html형식으로 넣어줍니다. (미리보기 사진 올리기 위함)

- main.ejs 의 img-preview 다음에 jquery 로 html문을 추가해줍니다.

 

참 파일저장 폴더는 /upload인데 경로 /img로 한 이유는 app.js에서 /img는 정적폴더인 /upload로 가게 되어있습니다.

 

 

3. post.js (NodeJS 서버쪽입니다. 프레임워크는 Express 사용)

const multer = require("multer");
const path = require("path");
const fs = require("fs");

fs.readdir("uploads", (error) => {
  if (error) {
    console.error("uploads폴더가 없어 uploads폴더를 생성합니다.");
    fs.mkdirSync("uploads");
  }
});

const upload = multer({
  storage: multer.diskStorage({
    destination(req, file, cb) {
      cb(null, "uploads/");
    },
    filename(req, file, cb) {
      const ext = path.extname(file.originalname);
      cb(null, path.basename(file.originalname, ext) + Date.now() + ext);
    },
  }),
  limits: { fileSize: 5 * 1024 * 1024 },
});

// 사진 upload에 저장하고 전달해서 미리보기
router.post("/img", upload.array("img", 4), (req, res) => {
  console.log("파일 이름 : ", req.files);
  let urlArr = new Array();
  for (let i = 0; i < req.files.length; i++) {
    urlArr.push(`/img/${req.files[i].filename}`);
    console.log(urlArr[i]);
  }
  let jsonUrl = JSON.stringify(urlArr);
  res.json(jsonUrl);
});

const upload2 = multer();

//none() 쓰면 오류나서 array씀
router.post("/", upload2.array("img", 4), async (req, res, next) => {
    console.log(req.body.content);
    console.log(req.body.url); //올린 사진 개수에 따라 배열로 나옴
    try {
      const post = await Post.create({
        content: req.body.content,
        //img: req.body.url,
        userId: req.user.id,
      });
      res.redirect("/");
    } catch (error) {
      console.error(error);
      next(error);
    }
  }
);

- 파일은 /upload 폴더에 저장합니다. 폴더가 없으면 생성합니다.

- 변수 upload는 multer 설정변수입니다. 파일 이름은 중복되면 안되니까 올린 시간을 붙여줍니다.

- router.post("img")는 multer 미들웨어를 사용합니다. 다중 파일이니까 array("img",4)써줍니다.

  img는 main.ejs에서 <input type="file" name="img" >의  name을 가져온겁니다. 4는 파일 최대 개수.

- ajax로 받아온 이미지파일이 담긴 formdata를 multer로 설정한 파일이름과 경로를 for문으로 돌려서 클라이언트에

  보내줄 배열에 저장합니다 배열을 JSON으로 변환시켜서 보내줍니다.

- router.post("/")는 main.ejs에서 submit 버튼을 눌렀을 때 form을 가져옵니다.

  첨부파일 외 다른 나머지는 req.body로 받아옵니다. 

- img url은 업로드한 개수만큼의 배열로 받아집니다. for문 돌려서 img db에 추가하면 됩니다!

 

 

여러개 선택도 가능하고 한개씩 선택해서 미리보기에 추가 가능합니다.
사진이 너무 길어서 짤렸지만 미리보기 두 장입니다.
서버에서 console.log(req.body.url); 한 것입니다.

 

 

 

 

다중 파일 저장하기 위해선 post table에 있던 img 컬럼을 .

img table을 새로 만들어서 저장해야합니다.