66. spring(9)

sumin·2023년 10월 15일
0

아카데미

목록 보기
67/82
post-thumbnail

File upload

pom.xml

파일 업로드를 하기 위해서는 디펜던시를 추가해 주어야 한다.

jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
  .ck.ck-editor {
    max-width: 800px;
  }
  .ck-editor__editable {
    min-height: 400px;
  }
  .ck-content {
    font-size: 12px;
    color: orange;
  }
</style>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script src="https://cdn.ckeditor.com/ckeditor5/40.0.0/classic/ckeditor.js"></script>
<script>

  $(function(){     
    fnFileCheck();
    fnUpload();
    fnCkeditor();
  })
  
  function fnFileCheck(){
    $('.files').change(function(){
      console.log(this.files);
      $('#file_list').empty();
      var maxSize = 1024 * 1024 * 100;
      var maxSizePerFile = 1024 * 1024 * 10;
      var totalSize = 0;
      var files = this.files;
      for(let i = 0; i < files.length; i++){
        totalSize += files[i].size;
        if(files[i].size > maxSizePerFile){
          alert('각 첨부파일의 최대 크기는 10MB입니다.');
          $(this).val('');
          $('#file_list').empty();
          return;
        }
        $('#file_list').append('<div>' + files[i].name + '</div>');
      }
      if(totalSize > maxSize){
        alert('전체 첨부파일의 최대 크기는 100MB입니다.');
        $(this).val('');
        $('#file_list').empty();
        return;
      }
    })
  }
  
  function fnUpload(){
    $('#btn_upload').click(function(){
      // ajax 파일 첨부는 FormData 객체를 생성해서 data로 전달한다.
      var formData = new FormData();
      var files = $('#files').prop('files');  // var files = $('#files')[0].files;  
      $.each(files, function(i, elem){          
        formData.append('files', elem);
      })
      // ajax
      $.ajax({
        // 요청
        type: 'post',
        url: '${contextPath}/ajax/upload.do',
        data: formData,
        contentType: false,
        processData: false,
        // 응답
        dataType: 'json',
        success: function(resData){  // resData === {"success":true}
          if(resData.success){
            alert('성공');
          } else {
            alert('실패');
          }
        }
      })
    })
  }

  function fnCkeditor(){
	 
	  ClassicEditor
	    .create(document.getElementById('contents'), {
		    toolbar: {
			    items: [
		        'undo', 'redo',
		        '|', 'heading',
		        '|', 'fontfamily', 'fontsize', 'fontColor', 'fontBackgroundColor',
		        '|', 'bold', 'italic', 'strikethrough', 'subscript', 'superscript', 'code',
		        '|', 'link', 'uploadImage', 'blockQuote', 'codeBlock',
		        '|', 'bulletedList', 'numberedList', 'todoList', 'outdent', 'indent'
  		    ],
  		    shouldNotGroupWhenFull: false
  	   },
       heading: {
         options: [
           { model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
           { model: 'heading1', view: 'h1', title: 'Heading 1', class: 'ck-heading_heading1' },
           { model: 'heading2', view: 'h2', title: 'Heading 2', class: 'ck-heading_heading2' },
           { model: 'heading3', view: 'h3', title: 'Heading 3', class: 'ck-heading_heading3' },
           { model: 'heading4', view: 'h4', title: 'Heading 4', class: 'ck-heading_heading4' },
           { model: 'heading5', view: 'h5', title: 'Heading 5', class: 'ck-heading_heading5' },
           { model: 'heading6', view: 'h6', title: 'Heading 6', class: 'ck-heading_heading6' }
         ]
       },
       ckfinder: {
    	   // 업로드 경로
    	   uploadUrl: '${contextPath}/ckeditor/upload.do'
       }
	   })
	   .catch(err => {
		   console.log(err)
	   });
  }
  
</script>
</head>
<body>

  <div>
    <h3>MVC 파일첨부</h3>
    <form method="post" action="${contextPath}/upload.do" enctype="multipart/form-data">
      <div>
        <input type="file" name="files" class="files" multiple>
      </div>
      <div>
        <button type="submit">업로드</button>
      </div>
    </form>
  </div>
  
  <hr>
  <div id="file_list"></div>
  <hr>
  
  <div>
    <h3>ajax 파일첨부</h3>
    <div>
      <input type="file" class="files" id="files" multiple>
    </div>
    <div>
      <button type="button" id="btn_upload">업로드</button>
    </div>
  </div>
  
  <hr>
  
  <div>
    <h3>CKEditor</h3>
    <form method="post" action="${contextPath}/add.do">
      <div>
        <textarea id="contents" name="contents"></textarea>
      </div>
      <div>
        <button type="submit">전송</button>
      </div>
    </form>
  </div>

</body>
</html>

문서편집기능 - ckeditor사용

config

controller

service


@RequiredArgsConstructor
@Service
public class FileServiceImpl implements FileService {
  
  private final MyFileUtil myFileUtil;

  @Override
  public int upload(MultipartHttpServletRequest multipartRequest) {
   
    //첨부된 파일들의 목록리스트
    List<MultipartFile> files = multipartRequest.getFiles("files");
    
    //순회
    for(MultipartFile multipartFile : files) {
      // 첨부 여부 확인
      if(multipartFile != null && ! multipartFile.isEmpty()) {
        
        try{
          
          // 첨부 파일이 저장될 경로 가져오기 
          String path = myFileUtil.getPath();
          
          // 저장된 경로의 디렉터리 
          File dir = new File(path);
          if(!dir.exists()) {
           dir.mkdirs();
          }
          
          // 첨부 파일의 원래 이름 알아내기 
          String originalName = multipartFile.getOriginalFilename();
          
          // 첨부파일이 저장될 이름가져오기
          String filesystemName = myFileUtil.getFilesystemName(originalName);
          
          // 첨부파일의 file 객체 
          File file = new File(dir, filesystemName);
          
          // 첨부파일 저장하기 
          multipartFile.transferTo(file);
          
          
        } catch(Exception e) {
          e.printStackTrace();
        }
      }
    }
    return 0;  // 첨부한 파일의 개수 
  }

  @Override
  public Map<String, Object> ajaxUpload(MultipartHttpServletRequest multipartRequest) {
    //첨부된 파일들의 목록리스트
    List<MultipartFile> files = multipartRequest.getFiles("files");
    
    //순회
    for(MultipartFile multipartFile : files) {
      // 첨부 여부 확인
      if(multipartFile != null && ! multipartFile.isEmpty()) {
        
        try{
          
          // 첨부 파일이 저장될 경로 가져오기 
          String path = myFileUtil.getPath();
          
          // 저장된 경로의 디렉터리 
          File dir = new File(path);
          if(!dir.exists()) {
           dir.mkdirs();
          }
          
          // 첨부 파일의 원래 이름 알아내기 
          String originalName = multipartFile.getOriginalFilename();
          
          // 첨부파일이 저장될 이름가져오기
          String filesystemName = myFileUtil.getFilesystemName(originalName);
          
          // 첨부파일의 file 객체 
          File file = new File(dir, filesystemName);
          
          // 첨부파일 저장하기 
          multipartFile.transferTo(file);
          
          //
          
        } catch(Exception e) {
          e.printStackTrace();
        }   
      }
    }
    return Map.of("success",true);  // 첨부한 파일의 개수 
  }
  
  @Override
  public Map<String, Object> ckeditorUpload(MultipartFile upload, String contextPath) {
    
    // 이미지 저장할 경로
    String path = myFileUtil.getPath();
    File dir = new File(path);
    if(!dir.exists()) {
      dir.mkdirs();
    }
    
    // 이미지 저장할 이름(원래 이름 + 저장할 이름)
    String originalFilename = upload.getOriginalFilename();
    String filesystemName = myFileUtil.getFilesystemName(originalFilename);
    
    // 이미지 File 객체
    File file = new File(dir, filesystemName);
    
    // File 객체를 참고하여, MultipartFile upload 첨부 이미지 저장
    try {
      upload.transferTo(file);
    } catch(Exception e) {
      e.printStackTrace();
    }
    
    //CKeditor로 저장된 이미지를 확인할 수 있는 경로를  {url: http://localhost:8080/...} 방식으로 반환해야함
    return Map.of("url", contextPath  + path + "/" + filesystemName
                , "uploaded", true);
  }
}

util

@Component
public class MyFileUtil {

  // 파일이 저장된 경로 반환하기 
  public String getPath() {
    /*  /storage/yyyy/MM/dd  */
    LocalDate today = LocalDate.now();
    return "/storage/" + today.getYear() + "/" + String.format("%02d", today.getMonthValue()) + "/" +String.format("%02d", today.getDayOfMonth()); 
    //return "/storage/" + DateTimeFormatter.ofPattern("yyyy/MM/dd").format(today);
    
  }
  
  // 파일이 저장될 이름 반환하기 
  public String getFilesystemName(String originalName) {
    
    /* UUID.확장자 */
    
    String extName = null;
    if(originalName.endsWith("tar.gz")) { // 확장자에 마침표가 포함되는 예외 경우를 처리한다. 
      extName = "tar.gz";      
    } else {
      String[] arr = originalName.split("\\.");  // [.] 또는 \\.
      extName = arr[arr.length - 1];
    } 
    return UUID.randomUUID().toString().replace("-", "") + "." + extName;
  }  
}

servlet-context.xml

profile
백엔드 준비생의 막 블로그

0개의 댓글