sm 기술 블로그

[스프링부트] 다중 파일 업로드 본문

스프링부트

[스프링부트] 다중 파일 업로드

sm_hope 2022. 8. 10. 22:40

html

        <div>
            <input type="file" id="btnAtt" name="multiFile" multiple/>
            <div id='att_zone' data-placeholder='파일을 첨부 하려면 파일 선택 버튼을 클릭하세요'></div>
        </div>

mulilple로 인해 다중 파일이 업로드 가능하다.

 

script

 <script>
// 여기는 솔직히 복붙이라 잘 몰라요 https://jobtc.tistory.com/43 여기서 퍼왔어요

(imageView = function imageView(att_zone, btn){

    var attZone = document.getElementById(att_zone);
    var btnAtt = document.getElementById(btn)
    var sel_files = [];

    // 이미지와 체크 박스를 감싸고 있는 div 속성
    var div_style = 'display:inline-block;position:relative;'
                  + 'width:150px;height:120px;margin:5px;border:1px solid #00f;z-index:1';
    // 미리보기 이미지 속성
    var img_style = 'width:100%;height:100%;z-index:none';
    // 이미지안에 표시되는 체크박스의 속성
    var chk_style = 'width:30px;height:30px;position:absolute;font-size:24px;'
                  + 'right:0px;bottom:0px;z-index:999;background-color:rgba(255,255,255,0.1);color:#f00';

    btnAtt.onchange = function(e){
      var files = e.target.files;
      var fileArr = Array.prototype.slice.call(files)
      for(f of fileArr){
        imageLoader(f);
      }
    }


    // 탐색기에서 드래그앤 드롭 사용
    attZone.addEventListener('dragenter', function(e){
      e.preventDefault();
      e.stopPropagation();
    }, false);

    attZone.addEventListener('dragover', function(e){
      e.preventDefault();
      e.stopPropagation();

    }, false);

    attZone.addEventListener('drop', function(e){
      var files = {};
      e.preventDefault();
      e.stopPropagation();
      var dt = e.dataTransfer;
      files = dt.files;
      for(f of files){
        imageLoader(f);
      }

    }, false);



    /*첨부된 이미리즐을 배열에 넣고 미리보기 */
    imageLoader = function(file){
      sel_files.push(file);
      var reader = new FileReader();
      reader.onload = function(ee){
        let img = document.createElement('img');
        img.setAttribute('style', img_style);
        img.src = ee.target.result;
        attZone.appendChild(makeDiv(img, file));
      }

      reader.readAsDataURL(file);
    }

    /*첨부된 파일이 있는 경우 checkbox와 함께 attZone에 추가할 div를 만들어 반환 */
    makeDiv = function(img, file){
      var div = document.createElement('div');
      div.setAttribute('style', div_style)

      var btn = document.createElement('input');
      btn.setAttribute('type', 'button');
      btn.setAttribute('value', 'x');
      btn.setAttribute('delFile', file.name);
      btn.setAttribute('style', chk_style);
      btn.onclick = function(ev){
        var ele = ev.srcElement;
        var delFile = ele.getAttribute('delFile');
        for(var i=0 ;i<sel_files.length; i++){
          if(delFile== sel_files[i].name){
            sel_files.splice(i, 1);
          }
        }

        dt = new DataTransfer();
        for(f in sel_files) {
          var file = sel_files[f];
          dt.items.add(file);
        }
        btnAtt.files = dt.files;
        var p = ele.parentNode;
        attZone.removeChild(p);
      }
      div.appendChild(img);
      div.appendChild(btn);
      return div
    }
  }
)('att_zone', 'btnAtt');
    </script>

미리보기 또한 가능하다.

 

Service

@Service
@RequiredArgsConstructor
public class FileUploadService {
    private final FilesRepository filesRepository;
    private final ArticleService articleService;

    @Async
    public void doUpload(ArticleWriteForm articleWriteForm, List<MultipartFile> multiFileList, HttpServletRequest request, HttpSession session) {

        String root = System.getProperty("user.dir") + "\\src\\main\\resources\\static\\uploadFiles";

        // 아래 두 줄은 경로가 실제로 존재하는지 확인한다.
        // 만약 실제로 존재하지 않는다면 폴더를 생성한다. (mkdir가 폴더 생성이다.)
        File fileCheck = new File(root);
        if (!fileCheck.exists()) fileCheck.mkdirs();

        //원래 파일의 이름 -> 수정된 파일의 이름을 여러개 담기위해 리스트로 만듬
        List<Map<String, String>> fileList = new ArrayList<>();

        for (int i = 0; i < multiFileList.size(); i++) {
            // 파일의 본래이름을 가져옴
            String originFile = multiFileList.get(i).getOriginalFilename();
            // 파일형식을 ext에 뽑아냄 ex) 나는이미지.png 에서 .png를 뽑아서 ext에 저장
            String ext = originFile.substring(originFile.lastIndexOf("."));
            // uuid는 네트워크 상에서 고유성이 보장되는 id를 만들기 위한 규약이다.
            // 다시말해 파일들이 서로 이름을 겹치지 않게 하기 위해서 만든것이다. 마지막에 ext를 붙여 파일형식을 정의해준다.
            String changeFile = UUID.randomUUID().toString() + ext;

            // 본래 이름을 Key, 바꾼 이름을 Value로 해서 map에 저장한다.
            Map<String, String> map = new HashMap<>();
            map.put("originFile", originFile);
            map.put("changeFile", changeFile);

            // 수정된 파일의 이름들을 담고 있는 map을 리스트에 저장한.
            fileList.add(map);
        }

        // 파일 업로드를 진행함
        try {
            for (int i = 0; i < multiFileList.size(); i++) {
                /*
                transferTo 는 파일을 저장하는 함수이다.
                파일 리스트에서 파일을 뽑고, 그 파일을 지정한 경로에 저장한다.
                */
                File uploadFile = new File(root + "\\" + fileList.get(i).get("changeFile"));
                multiFileList.get(i).transferTo(uploadFile);
            }
            // 위 작업이 완료되었다면 성공적으로 파일을 저장했다는 뜻으로, 이제 글을 생성해야한다.
            // 아래에 있는 함수는 현재 클래스에서 지원한다.
            // 파일 목록, 세션정보, 받아온 제목과 내용을 넘긴다.
            uploadDB(fileList, session, articleWriteForm);
            // 확인용...
            System.out.println("다중 파일 업로드 성공!");

        } catch (IllegalStateException | IOException e) {
            // 확인용...
            System.out.println("다중 파일 업로드 실패 ㅠㅠ");
            // 만약 업로드 실패하면 파일 삭제
            for (int i = 0; i < multiFileList.size(); i++) {
                new File(root + "\\" + fileList.get(i).get("changeFile")).delete();
            }
            // 오류 추적하는거임
            e.printStackTrace();
        }
    }

    // 파일 DB에 업로드 할거임.
    private void uploadDB(List<Map<String, String>> fileName, HttpSession session, ArticleWriteForm articleWriteForm) {

        // 먼저 글쓰기를 진행함. 로그인한 아이디와 제목, 내용을 넘긴다. (로그인한 아이디를 넘긴 이유는 누가 글을 썼는지 알기 위해서이다.)
        Article aritcle = articleService.doWrite((long) session.getAttribute("loginedUserId"), articleWriteForm.getTitle(), articleWriteForm.getBody());
        // 파일을 만들어서 DB에 저장하는 과정
        for (Map<String, String> file : fileName) {
            Files files = new Files();
            files.setFilename(file.get("changeFile"));
            files.setArticle(aritcle);
            filesRepository.save(files);

        }
    }
}

 

DB

CREATE TABLE files(
    id BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    `filename` TEXT,
    article_id BIGINT UNSIGNED NOT NULL //파일들을 연결하고자 하는 객체의 아이디
);
Comments