Spring 다중 파일 업로드

Jehyun·2023년 5월 9일
0

이번에 홈페이지 제작하면서 게시판 파일 업로드와 관련해서 너무 많은 어려움을 겪었기 때문에 그와 관련해서 정리하고, 다시 공부하는 느낌으로 글을 쓰려고 한다.
우선 Spring을 통한 개발이 약 4개월차에 접어들었기 때문에 코드가 난잡할 수 있다는 점을 이해 해주길 바람

파일 전송을 위해서 multipart와 관련된 설정은 다른 블로그에도 많이 있으니 참고 바람

boardWriteForm

  • 이미지나 영상 등 파일을 전송하기 위해서는 enctype=multipart/form-data로 설정해야함
  • 단일 파일이 아닌 다중 파일의 경우 input type="file" 이후 multiple 옵션을 줘야한다
  • submit()한 값을 받기 위해서는 입력 데이터 name을 반드시 표시 해줘야한다.

FileList

file의 경우 fileList의 형태로 저장이 되는데 fileList의 경우 배열이 아니다

형태는 대충 이러하며 name, size 등 여러가지 데이터를 담고 있다. fileList의 경우 보안상의 이유로 수정이 불가능하고, value값으로 지정이 불가능하다.

ex) 예를 들어 2개의 파일만 올려야하는데 3개의 파일을 올렸다고 가정하자 그렇다면 1개의 파일을 지워야하는데 그 데이터를 지우기 위해서 file을 다시 선택해야 한다.
fileList는 수정이 불가능하지만 배열과 굉장히 비슷한 형태를 하고 있기 때문에 이 데이터를 배열로 잠시 바꿔주는 과정을 거친다.

removeFile

Jquery를 이용한 파일 업로드 출력 및 삭제

const handler = {
           init() {
               const fileInput = document.querySelector('#forum-file');
               const preview = document.querySelector('#preview');
               fileInput.addEventListener('change', () => {  
                   console.dir(fileInput)
                   const files = Array.from(fileInput.files)
                   files.forEach(file => {
                	   console.log(file)
                	   console.log(file.size)
                	   console.log(file.lastModifiedDate)
                	   
                	   var setDate = new Date()
                	   var cDate = getDate(setDate)
                	   
                       preview.innerHTML += `<p id=`+file.lastModified+`>`+file.name+` ` +
                       file.size+ 'byte' + ` ` + cDate + ` <button data-index=`+file.lastModified +
                       ` class='file-remove'>제거</button></p>`;
                   });
               });
           },
           removeFile: () => {
               document.addEventListener('click', (e) => {
               if(e.target.className !== 'file-remove')
            	   return;
               const removeTargetId = e.target.dataset.index;
               const removeTarget = document.getElementById(removeTargetId);
               const files = document.querySelector('#forum-file').files;
               const dataTranster = new DataTransfer();

               // document.querySelector('#file-input').files =
               //             Array.from(files).filter(file => file.lastModified !== removeTarget);

               Array.from(files).filter(file => file.lastModified != removeTargetId)
                   .forEach(file => { dataTranster.items.add(file);
                });
   
               document.querySelector('#forum-file').files = dataTranster.files;

               removeTarget.remove();
           })
           }
       }

원본 사이트 : https://coco-log.tistory.com/161

해당 코드를 설명하면 input file로 받아온 file의 data를 받아와서 배열로 바꿔준다. 이후 fileList의 데이터를 하나씩 가져와서 view에 추가해주는 과정을 거친다.
Front에 소질없는 인간

이런식으로 하단에 이름, 크기, 생성일, 제거 버튼을 추가해준다.

다음 removeFile로 내려와서 보면 해당 button의 class가 내가 원하는 class가 아니면 return 시켜버리고, 그게 아니면 지우고 싶은 file 데이터의 p 태그 id 값을 가져온다. 삭제 시키고자 하는 값이 아닐 경우에는 dataTranster을 이용해 file의 형태로 저장하고 그 값을 내가 처음 input을 이용해 파일을 넣었던 곳에 넣어준다. 마지막으로 지우고 싶은 태그는 remove 함수를 통해서 삭제 시켜준다.
삭제 이후

JSP는 이것이 전부이다.

Controller

다음은 Controller쪽으로 넘어가서

Controller

postMapping으로 받아야하며(multipart/form-data는 애초에 post에서만 사용이 가능함) 필수요소는 MultipartHttpServletRequest 와 HttpServletRequest는 반드시 받아와야 한다. multi의 경우 저 안에 모든 데이터들이 모여 있기 때문에 없어서는 안된다. 자료구조의 형태는 본인이 편한 것들을 사용하면 된다. ArrayList에 받아 오던 사람도 있고, 지금처럼 통채로 가져와서 Service쪽에서 나눠서 사용하는 경우도 있다.

파일 저장 경로

파일 경로는 보통 resources 하위 폴더에 저장한다. req.getSession().getServletContext().getRealPath("/resources/")를 통해서 resources의 경로를 받아온다. 실제경로이기 때문에 저 값을 출력하면 Tomcat이 돌아가면서 파일들이 저장되어 있고 있는 디렉토리의 경로를 찍어준다. 절대경로로 다 찍어주지 않으면 이후에 Server에서 돌릴 때 경로를 다시 맞춰야하기 때문에 상대경로로 파일을 저장하는 것은 추천하지 않는다.

Service

다음은 데이터들을 처리해보자

boardWrite

multi에 저장되어 있는 Parameter값은 input 태그에 적었던 name을 통해서 가져올 수 있다. 전달 받은 데이터를 ForumDTO(boardDTO)를 만들어서 저장하고 저장시킨다.
(지금 보면 딱히 필요없는 코드들도 눈에 보인다 "title==null" 이런 문장은 JS에서 즉각적으로 처리하는게 더 좋아보이네요)

File처리

ArrayList로 fileList들을 받아온다

이후에 fileList들을 출력해 보면 MultipartFile들이 들어가 있다.

파일을 저장하기 위한 path는 필요한 게시글에 맞게 저장하기 위해서 추가로 bType과 writer를 추가한다(이것도 지금와서 보니 writer는 필요없어 보이네요...)
file을 넣으려고 하는데 디렉토리가 없으면 mkdir()을 통해서 디렉토리를 만들어준다.

fileList로 받아온 데이터를 저장할 HashMap setFileList 생성 파일 업로드 할 때 사용할 것이다. 이유는 multi를 통해 얻어 온 fileList에 있는 OriginalFileName의 경우에는 그대로 파일 업로드 했을 경우 A파일을 가진 1번 게시글과 2번 게시글이 있다 이 때 1번 게시판을 삭제하면서 A파일을 지운다면 2번 게시글에 있는 A파일도 동시에 지워지게 된다. 따라서 UUID를 이용해 네트워크 상에 존재하는 고유값으로 파일을 저장하면 원본은 A파일이지만 이름은 서로 다른 2개의 파일이 존재하게 되기 때문에 1번 게시판을 지워도 2번 게시판에서 파일은 그대로 보존된다.
(HashMap을 이용하여 Mapper에서 query문을 작성해도 되지만 해당 프로젝트 작업 당시 selectkey에 오류가 많이 떠서 FileDTO를 사용해서 데이터를 넣어주었다 에러없이 진행된다면 Map을 이용한 작성이 보다 깔끔하고 보기 편한 코딩이 가능하다)

파일 업로드 파트이다. 아까 setFileList로 저장한 OriginalfName이 NULL이 아니면 UUID로 생성한 fileName을 이용하여 file을 path에 업로드 시킨다.

profile
주니어 개발자

0개의 댓글