본문 바로가기
Spring

[Spring] 파일 업로드(9) 에디터로 파일업로드

by moca7 2024. 10. 25.

 

 

 

ㅁ main.jsp

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
   
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>


<style>
  form > label {
    font-size: 12px;
    color: gray;
  }
</style>

</head>
<body>

    <script>
      $(function(){
       
        $("input[type=file]").on('change', function(evt){
         
          const files = evt.target.files; // FileList {0:File, 1:File, ...}
          console.log(files);
       
         
          let totalSize = 0;
         
          for(let i=0; i<files.length; i++){
            if(files[i].size > 10 * 1024 * 1024) {
              alert("첨부파일의 최대 크기는 10MB입니다.");
              evt.target.value = ""; // 선택한 파일 초기화
              return; // 10MB 초과라면 여기서 function 빠져나가기
            }
          }
         
         
          totalSize += files[i].size; // 선택한 전체 파일 용량 알아내기
         
          if(totalSize > 100 * 1024 * 1024) {
            alert("전체 첨부파일 최대 크기는 100MB입니다.");
            evt.target.value = ""; // 선택한 파일 초기화
            return; // function 빠져나가기
          }
         
        })
       
      })
    </script>


    <h2>1. 한 개의 첨부파일 업로드 테스트</h2>
    <form action="${ contextPath }/board/insert1.do" method="post" enctype="multipart/form-data">
      게시글 제목 : <input type="text" name="boardTitle"> <br>
      게시글 내용 : <textarea name="boardContent"></textarea> <br>
      첨부파일 : <input type="file" name="uploadFile"> <br>
      <label>첨부파일 사이즈는 10MB 이하여야 됩니다.</label> <br><br>
     
      <button type="submit">등록</button>
    </form>
   

    <h2>2. 다중 첨부파일 업로드 테스트</h2>
    <form action="${ contextPath }/board/insert2.do" method="post" enctype="multipart/form-data">
      게시글 제목 : <input type="text" name="boardTitle"> <br>
      게시글 내용 : <textarea name="boardContent"></textarea> <br>
      첨부파일 : <input type="file" name="uploadFile" multiple> <br>
      <label>각 첨부파일 사이즈는 10MB 이하, 총 100MB 이하여야 됩니다.</label> <br><br>
     
      <button type="submit">등록</button>
    </form>
   
   

    <h2>3. 비동기식으로 첨부파일 업로드 테스트</h2>
    <div id="async_text">
      게시글 제목 : <input type="text" id="title"> <br>
      게시글 내용 : <textarea id="content"></textarea> <br>
      첨부파일 : <input type="file" id="file"> <br><br>
     
      <button id="ajax_btn">등록</button>
    </div>
   
   
    <script>
      $(function(){
        $("#ajax_btn").on("click", function(){
         
          // ajax 게시글 등록 (요청시 전달값 : 제목, 내용, 첨부파일)
          // 첨부파일을 전달해야할 경우 자바스크립트의 FormData 객체(가상의 form요소다.)에 담아서 전달
          let formData = new FormData();
          formData.append("boardTitle", document.getElementById("title").value);
          formData.append("boardContent", document.getElementById("content").value);
          formData.append("uploadFile", document.getElementById("file").files[0]); // File객체 담기
         
          $.ajax({
            url: '${contextPath}/board/ajaxInsert.do',
            type: 'post', // 파일 넘어갈땐 항상 post 방식
            data: formData,
           
            processData: false, // processData : false 선언시 formData를 string으로 변환하지 않음.
            contentType: false, // contentType : false 선언시 multipart/form-data로 전송되게 하는 옵션.
            success: function(result){
              if(result == "SUCCESS"){
                alert("성공적으로 등록!");
              }else{
                alert("등록 실패!");
              }
            },
            error: function(){
             
            }
          })
         
        })
      })
    </script>
   
   
   

    <h2>4. 첨부파일 목록 조회</h2>
    <button onclick="fn_selectAttachList();">첨부파일 조회</button>
   
    <div id="result"></div>
   
   
    <script>
      function fn_selectAttachList(){
        $.ajax({
          url: '${contextPath}/board/atlist.do',
          success: function(resData){
            console.log(resData); // [{}, {}]
           
            let a = '';
            for(let i=0; i<resData.length; i++){
              a += '<a href="${contextPath}' + resData[i].filePath + '/' + resData[i].filesystemName + '" download="' + resData[i].originalName + '">' + resData[i].originalName + '</a><br>'
            }
           
            $('#result').html(a);

          }
        })
      }
    </script>



    <hr>
   
    <h2>번외. 내용 작성란에 에디터 적용시키기</h2>
    <p>
      에디터 기능 사용시 내용을 작성할 때 폰트 수정, 정렬, 이미지 삽입 등등이 가능하다. <br>
      위의 과정들이 반영된 html 요소가 넘어감 => 그걸 DB에 기록했다가 조회해서 화면에 뿌리면 시각적 요소가 적용된 채로 보여진다. <br>
     
      - 에디터 api 종류 : summernote, ckeditor 등등
    </p>


    <form action="${contextPath }/board/editor/insert.do" method="post">
      게시글 제목 : <input type="text" name="boardTitle"> <br>
      게시글 내용 : <textarea id="summernote" name="boardContent"></textarea> <br><br>
      <button type="submit">등록</button>
    </form>
   

    <!-- include libraries(jQuery, bootstrap) -->
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
   
    <!-- include summernote css/js -->
   
    <script>
      $(document).ready(function() {
        $('#summernote').summernote({
          width: 500,
          height:500,
          placeholder: 'hello world'
        });
      });
    </script>
   
</body>
</html>
 

 

- post 방식으로 넘긴다. 넘길 게 엄청 많다. 

- boardTitle, boardContent를 줬다. BoardDto의 필드명과 같다.

 

 

 

 

 

 

 

ㅁ BoardController

 

 
package com.br.file.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;
import com.br.file.service.BoardService;
import com.br.file.util.FileUtil;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequestMapping("/board")
@RequiredArgsConstructor
@Controller
public class BoardController {

  private final BoardService boardService;
  private final FileUtil fileUtil;
 
 
  @PostMapping("/insert1.do")
  public String insertOneFileBoard(BoardDto board, MultipartFile uploadFile) {
   
    log.debug("board: {}", board);      // 잘 담겼는지 확인용 로그 출력
    log.debug("attach: {}", uploadFile);  // 잘 담겼는지 확인용 로그 출력
   
   
    AttachDto attach = null;
   
    if(uploadFile != null && !uploadFile.isEmpty()) { // 첨부파일이 존재할 경우 => 업로드
      // 전달된 파일 업로드 처리
     

      // FileUitl 클래스의 fileupload 메소드를 호출 (uploadFile을 넘기면서)
      Map<String, String> map = fileUtil.fileupload(uploadFile);
     
     
      // (4) db에 기록할 정보를 자바 객체(AttachDto)에 세팅
      attach = AttachDto.builder()
                .filePath(map.get("filePath"))
                .originalName(map.get("originalFilename"))
                .filesystemName(map.get("filesystemName"))
                .build();
     
    }
   


    int result = boardService.insertOneFileBoard(board, attach);
   
    if(result > 0) {
      log.debug("게시글 등록 성공");
    }else {
      log.debug("게시글 등록 실패");
    }

    return "redirect:/"; // 성공이든 실패든 메인페이지 요청
   
  }
 
 
 
 
 
  @PostMapping("/insert2.do")
  public String insertManyFileBoard(BoardDto board, List<MultipartFile> uploadFile) {
   
    List<AttachDto> list = new ArrayList<>();
   
   
    for(MultipartFile file : uploadFile) {
     
      if(file != null && !file.isEmpty()) { // 파일이 존재할 경우
       
        Map<String, String> map = fileUtil.fileupload(file);
        list.add(AttachDto.builder()
                  .filePath(map.get("filePath"))
                  .originalName(map.get("originalFilename"))
                  .filesystemName(map.get("filesystemName"))
                  .build());
      }
    }
   
   
    // 넘어온 첨부파일이 없었다면 list는 비어있는 상태다.
    int result = boardService.insertManyFileBoard(board, list);
   
    // 첨부파일이 없었다면 result는 board에만 insert되고 result는 1이다.
    // 첨부파일이 있었다면 첨부파일의 개수만큼
    if(list.isEmpty() && result == 1 || !list.isEmpty() && result == list.size()) {
      log.debug("다중 첨부파일 게시글 등록 성공");
    }else {
      log.debug("다중 첨부파일 게시글 등록 실패");
    }
   
   
    return "redirect:/"; // 성공이든 실패든 메인페이지 요청
  }
 
 
  @ResponseBody
  @PostMapping("/ajaxInsert.do")
  public String insertAjaxFileBoard(BoardDto board, MultipartFile uploadFile) {
   
 
    AttachDto attach = null;
   
    if(uploadFile != null && !uploadFile.isEmpty()) {
     
      Map<String, String> map = fileUtil.fileupload(uploadFile);
     
      attach = AttachDto.builder()
                .filePath(map.get("filePath"))
                .originalName(map.get("originalFilename"))
                .filesystemName(map.get("filesystemName"))
                .build();
    }
   
   
    int result = boardService.insertOneFileBoard(board, attach);
   
    if(result > 0) {
      log.debug("ajax 첨부파일 게시글 등록 성공");
      return "SUCCESS";
    }else {
      log.debug("ajax 첨부파일 게시글 등록 실패");
      return "FAIL";
    }
   
  }
 
 
  @ResponseBody
  @GetMapping(value="/atlist.do", produces="application/json")
  public List<AttachDto> selectAttachList() {
   
    return boardService.selectAttachList(); // success 함수의 매개변수로 리턴된다.
  }
 
 
  @PostMapping("/editor/insert.do")
  public String editorInsertBoard(BoardDto board) {
   
    log.debug("board: {}", board);
   
    return "redirect:/";
  }
 
 
}
 

 

 

 

 

 

 

 

 

 

 

 

==================================================================================

 

 

 

 

- 그런데 varchar2 4000바이트로도 모자랄 수 있다. html 태그까지도 같이 넘어가기 때문에 4천 글자를 보관하는 4천 바이트도 모자랄 수 있다.

- 그래서 통상 그것보다도 더 범위가 큰 시롭이라는 컬럼 타입을 쓴다.

- 통상적으로 에디터 쓸 때는 CLOB 타입을 쓰곤 한다.

 

 

 

 

 

- 아예 컬럼을 삭제한다.

- 자바는 그냥 String 타입 그대로 두면 된다.

 

 

 

 

ㅁ BoardController

 

 
package com.br.file.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;
import com.br.file.service.BoardService;
import com.br.file.util.FileUtil;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequestMapping("/board")
@RequiredArgsConstructor
@Controller
public class BoardController {

  private final BoardService boardService;
  private final FileUtil fileUtil;
 
 
  @PostMapping("/insert1.do")
  public String insertOneFileBoard(BoardDto board, MultipartFile uploadFile) {
   
    log.debug("board: {}", board);      // 잘 담겼는지 확인용 로그 출력
    log.debug("attach: {}", uploadFile);  // 잘 담겼는지 확인용 로그 출력
   
   
    AttachDto attach = null;
   
    if(uploadFile != null && !uploadFile.isEmpty()) { // 첨부파일이 존재할 경우 => 업로드
      // 전달된 파일 업로드 처리
     

      // FileUitl 클래스의 fileupload 메소드를 호출 (uploadFile을 넘기면서)
      Map<String, String> map = fileUtil.fileupload(uploadFile);
     
     
      // (4) db에 기록할 정보를 자바 객체(AttachDto)에 세팅
      attach = AttachDto.builder()
                .filePath(map.get("filePath"))
                .originalName(map.get("originalFilename"))
                .filesystemName(map.get("filesystemName"))
                .build();
     
    }
   


    int result = boardService.insertOneFileBoard(board, attach);
   
    if(result > 0) {
      log.debug("게시글 등록 성공");
    }else {
      log.debug("게시글 등록 실패");
    }

    return "redirect:/"; // 성공이든 실패든 메인페이지 요청
   
  }
 
 
 
 
 
  @PostMapping("/insert2.do")
  public String insertManyFileBoard(BoardDto board, List<MultipartFile> uploadFile) {
   
    List<AttachDto> list = new ArrayList<>();
   
   
    for(MultipartFile file : uploadFile) {
     
      if(file != null && !file.isEmpty()) { // 파일이 존재할 경우
       
        Map<String, String> map = fileUtil.fileupload(file);
        list.add(AttachDto.builder()
                  .filePath(map.get("filePath"))
                  .originalName(map.get("originalFilename"))
                  .filesystemName(map.get("filesystemName"))
                  .build());
      }
    }
   
   
    // 넘어온 첨부파일이 없었다면 list는 비어있는 상태다.
    int result = boardService.insertManyFileBoard(board, list);
   
    // 첨부파일이 없었다면 result는 board에만 insert되고 result는 1이다.
    // 첨부파일이 있었다면 첨부파일의 개수만큼
    if(list.isEmpty() && result == 1 || !list.isEmpty() && result == list.size()) {
      log.debug("다중 첨부파일 게시글 등록 성공");
    }else {
      log.debug("다중 첨부파일 게시글 등록 실패");
    }
   
   
    return "redirect:/"; // 성공이든 실패든 메인페이지 요청
  }
 
 
  @ResponseBody
  @PostMapping("/ajaxInsert.do")
  public String insertAjaxFileBoard(BoardDto board, MultipartFile uploadFile) {
   
 
    AttachDto attach = null;
   
    if(uploadFile != null && !uploadFile.isEmpty()) {
     
      Map<String, String> map = fileUtil.fileupload(uploadFile);
     
      attach = AttachDto.builder()
                .filePath(map.get("filePath"))
                .originalName(map.get("originalFilename"))
                .filesystemName(map.get("filesystemName"))
                .build();
    }
   
   
    int result = boardService.insertOneFileBoard(board, attach);
   
    if(result > 0) {
      log.debug("ajax 첨부파일 게시글 등록 성공");
      return "SUCCESS";
    }else {
      log.debug("ajax 첨부파일 게시글 등록 실패");
      return "FAIL";
    }
   
  }
 
 
  @ResponseBody
  @GetMapping(value="/atlist.do", produces="application/json")
  public List<AttachDto> selectAttachList() {
   
    return boardService.selectAttachList(); // success 함수의 매개변수로 리턴된다.
  }
 
 
  @PostMapping("/editor/insert.do")
  public String editorInsertBoard(BoardDto board) {
   
    log.debug("board: {}", board);
    int result = boardService.insertOneFileBoard(board, null);
   
    if(result > 0) {
      log.debug("에디터 게시글 등록 성공");
    }else {
      log.debug("에디터 게시글 등록 실패");
    }
   
    return "redirect:/";
  }
 
 
}
 

 

- editorInsertBoard 메소드를 완성시킨다.  BoardServieImpl의 메소드를 재사용 한다.

 

- 실행하고 db에 잘 insert 되는지 확인한다.

 

 

 

 

 

 

 

 

9:25~ 9:30자리비웠음.

 

 

 

 

=====================================================================================

 

 

 

 

 

ㅁ main.jsp

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
   
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>    
<c:set var="contextPath" value="${pageContext.request.contextPath}" />
   
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>


<style>
  form > label {
    font-size: 12px;
    color: gray;
  }
</style>

</head>
<body>

    <script>
      $(function(){
       
        $("input[type=file]").on('change', function(evt){
         
          const files = evt.target.files; // FileList {0:File, 1:File, ...}
          console.log(files);
       
         
          let totalSize = 0;
         
          for(let i=0; i<files.length; i++){
            if(files[i].size > 10 * 1024 * 1024) {
              alert("첨부파일의 최대 크기는 10MB입니다.");
              evt.target.value = ""; // 선택한 파일 초기화
              return; // 10MB 초과라면 여기서 function 빠져나가기
            }
          }
         
         
          totalSize += files[i].size; // 선택한 전체 파일 용량 알아내기
         
          if(totalSize > 100 * 1024 * 1024) {
            alert("전체 첨부파일 최대 크기는 100MB입니다.");
            evt.target.value = ""; // 선택한 파일 초기화
            return; // function 빠져나가기
          }
         
        })
       
      })
    </script>


    <h2>1. 한 개의 첨부파일 업로드 테스트</h2>
    <form action="${ contextPath }/board/insert1.do" method="post" enctype="multipart/form-data">
      게시글 제목 : <input type="text" name="boardTitle"> <br>
      게시글 내용 : <textarea name="boardContent"></textarea> <br>
      첨부파일 : <input type="file" name="uploadFile"> <br>
      <label>첨부파일 사이즈는 10MB 이하여야 됩니다.</label> <br><br>
     
      <button type="submit">등록</button>
    </form>
   

    <h2>2. 다중 첨부파일 업로드 테스트</h2>
    <form action="${ contextPath }/board/insert2.do" method="post" enctype="multipart/form-data">
      게시글 제목 : <input type="text" name="boardTitle"> <br>
      게시글 내용 : <textarea name="boardContent"></textarea> <br>
      첨부파일 : <input type="file" name="uploadFile" multiple> <br>
      <label>각 첨부파일 사이즈는 10MB 이하, 총 100MB 이하여야 됩니다.</label> <br><br>
     
      <button type="submit">등록</button>
    </form>
   
   

    <h2>3. 비동기식으로 첨부파일 업로드 테스트</h2>
    <div id="async_text">
      게시글 제목 : <input type="text" id="title"> <br>
      게시글 내용 : <textarea id="content"></textarea> <br>
      첨부파일 : <input type="file" id="file"> <br><br>
     
      <button id="ajax_btn">등록</button>
    </div>
   
   
    <script>
      $(function(){
        $("#ajax_btn").on("click", function(){
         
          // ajax 게시글 등록 (요청시 전달값 : 제목, 내용, 첨부파일)
          // 첨부파일을 전달해야할 경우 자바스크립트의 FormData 객체(가상의 form요소다.)에 담아서 전달
          let formData = new FormData();
          formData.append("boardTitle", document.getElementById("title").value);
          formData.append("boardContent", document.getElementById("content").value);
          formData.append("uploadFile", document.getElementById("file").files[0]); // File객체 담기
         
          $.ajax({
            url: '${contextPath}/board/ajaxInsert.do',
            type: 'post', // 파일 넘어갈땐 항상 post 방식
            data: formData,
           
            processData: false, // processData : false 선언시 formData를 string으로 변환하지 않음.
            contentType: false, // contentType : false 선언시 multipart/form-data로 전송되게 하는 옵션.
            success: function(result){
              if(result == "SUCCESS"){
                alert("성공적으로 등록!");
              }else{
                alert("등록 실패!");
              }
            },
            error: function(){
             
            }
          })
         
        })
      })
    </script>
   
   
   

    <h2>4. 첨부파일 목록 조회</h2>
    <button onclick="fn_selectAttachList();">첨부파일 조회</button>
   
    <div id="result"></div>
   
   
    <script>
      function fn_selectAttachList(){
        $.ajax({
          url: '${contextPath}/board/atlist.do',
          success: function(resData){
            console.log(resData); // [{}, {}]
           
            let a = '';
            for(let i=0; i<resData.length; i++){
              a += '<a href="${contextPath}' + resData[i].filePath + '/' + resData[i].filesystemName + '" download="' + resData[i].originalName + '">' + resData[i].originalName + '</a><br>'
            }
           
            $('#result').html(a);

          }
        })
      }
    </script>



    <hr>
   
    <h2>번외. 내용 작성란에 에디터 적용시키기</h2>
    <p>
      에디터 기능 사용시 내용을 작성할 때 폰트 수정, 정렬, 이미지 삽입 등등이 가능하다. <br>
      위의 과정들이 반영된 html 요소가 넘어감 => 그걸 DB에 기록했다가 조회해서 화면에 뿌리면 시각적 요소가 적용된 채로 보여진다. <br>
     
      - 에디터 api 종류 : summernote, ckeditor 등등
    </p>


    <form action="${contextPath }/board/editor/insert.do" method="post">
      게시글 제목 : <input type="text" name="boardTitle"> <br>
      게시글 내용 : <textarea id="summernote" name="boardContent"></textarea> <br><br>
      <button type="submit">등록</button>
    </form>
   

    <!-- include libraries(jQuery, bootstrap) -->
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
   
    <!-- include summernote css/js -->
   
    <script>
      $(document).ready(function() {
        $('#summernote').summernote({
          width: 500,
          height:500,
          placeholder: 'hello world'
         
          callbacks: {
            onImageUpload: function(images){
             
              // 비동기식으로 이미지 업로드
              for(let i=0; i<images.length; i++){
               
                let formData = new FormData();
                formData.append('image', images[i]);
               
                $.ajax({
                  url: '${contextPath}/board/editor/imageUpload.do',
                  type: 'post',
                  data: formData,
                  processData: false,
                  contentType: false,
                 
                  async: false,
                  success: function(src){
                    // 파일 업로드 후에 저장경로+저장파일명을 응답데이터로 받아
                    // 현재 에디터의 <img>의 src 속성값을 응답데이터로 변경한다.
                   
                    $("#summernote").summernote('insertImage', '${contextPath}' + src);
                  }
                })
               
              }
             
            }
          }
       
        });
      });
    </script>
   
</body>
</html>
 

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
   form>label {
      font-size: 12px;
      color: gray;
   }
</style>
</head>
<body>
   <script>/* $(function): 요소가 다 만들어지고 스크립트실행  */
      $(function(){
         $("input[type=file]").on('change',function(evt){
            const files = evt.target.files;//FileList타입 {0:File,1:File..}
            console.log(files);
           
            let totalSize = 0;                   //File전체용량
           
            for(let i=0;i<files.length;i++){//File객체에 접근
               if(files[i].size > 10 * 1024 * 1024){//하나의 파일 크기가 10메가 초과일 경우
                  alert("첨부 파일의 최대 크기는 10MB.");
                  evt.target.value="";
                  return;  
               }
               totalSize += files[i].size;      //누적해서 파일 용량 더해주기
               
               if(totalSize > 100 * 1024 * 1024){//전체 첨부파일 사이즈가 100메가 초과일 경우
                  alert("총 용량의 최대 크기는 100MB입니다.");
                  evt.target.value="";
                  return;
               }
               
            }//for
           
         })/* 이런 function: eventHandler */
      })
   </script>
   
   <h2>1. 한 개의 첨부파일 업로드 테스트</h2>
   <form action="${ contextPath }/board/insert1.do" method="post" enctype="multipart/form-data">
      <!-- 파일이 파일로 넘어가게 하려면 enctype작성필요 -->
      게시글 제목 : <input type="text" name="boardTitle"><br>
      게시글 내용 : <textarea name="boardContent"></textarea><br>
      첨부파일 : <input type="file" name="uploadFile"><br>
      <label>첨부 파일 사이즈 10MB 이하</label><br><br>
     
      <button type="submit">등록</button>
   </form>
   
   <h2>2. 다중 첨부파일 업로드 테스트</h2>
   <form action="${ contextPath }/board/insert2.do" method="post" enctype="multipart/form-data">
      게시글 제목 : <input type="text" name="boardTitle"><br>
      게시글 내용 : <textarea name="boardContent"></textarea><br>
      첨부파일 : <input type="file" name="uploadFile" multiple><br>
      <label>각 첨부 파일 사이즈는 10MB 이하, 총100MB이하여야 합니다.</label><br><br>
     
      <button type="submit">등록</button>
   </form>  
   
   <h2>3. 비동기식 첨부파일 업로드 테스트</h2>
   <div id="async_test">
      게시글 제목 : <input type="text" id="title"><br>
      게시글 내용 : <textarea id="content"></textarea><br>
      첨부파일 : <input type="file" id="file"><br><br>
     
      <button type="button" id="submit">등록</button>
   </div>
   
   <script>
      $(function(){
         $("#submit").on("click",function(){
           
            //ajax게시글 등록(요청시 전달값 : 제목,내용,파일)
            //가상의 form요소에 담아서 전달
            //첨부파일 전달 해야 할 경우->FormData객체(가상의 form요소)에 담아서 전달
            let formData = new FormData();//javaScript객체
            formData.append("boardTitle",document.getElementById("title").value);
            formData.append("boardContent",document.getElementById("content").value);
            formData.append("uploadFile",document.getElementById("file").files[0]);//File객체 담기. files[]
           
            $.ajax({
               url: '${contextPath}/board/ajaxInsert.do',
               type: 'post',
               data: formData,//요청시 전달값 이미 담아놨음
               //첨부파일 기능 있을 시 ㅍ평소와 다르게 아래 두가지 넣음
               processData: false,//false선언시
               contentType: false,
               success: function(result){
                  if(result == "SUCCESS"){
                     alert("등록 성공");
                     location.reload();//새로고침 진행
                  }else{                    
                     alert("등록 실패");
                  }
               },
               error: function(){
                  console.log()
               }
            })
           
         })
      })
   </script>
   
   
   <h2>4. 첨부파일 목록 조회(비동기식)</h2>
   <button onclick="fn_selectAttachList();">조회</button>
   <div id="result">
     
   </div>
   
   <script>
      function fn_selectAttachList(){
         $.ajax({
            url: '${contextPath}/board/atlist.do',
            success: function(resData){
               console.log(resData);
               
               let a = '';
               for(let i=0;i<resData.length;i++){
//                  a += '<a href=${contextPath}'+resData[i].filePath+'/'+resData[i].filesystemName+'" download="'+resData[i].originalName+'">'+resData[i].originalName + '</a><br>';
                  a += '<a href="${contextPath}' + resData[i].filePath + '/' + resData[i].filesystemName + '" download="' + resData[i].originalName + '">' + resData[i].originalName + '</a><br>'
                     
                 
                 
                 
               }
               
               $("#result").html(a);
               
            }
         })
      }
   </script>
   
   <hr>
   <h2>번외. 내용 작성란에 에디터 적용 시키기</h2>
   <p>
      에디터 기능 사용 시 내용 작성 시 폰트 수정, 정렬, 이미지 삽입 등등이 가능함<br>
      위의 과정 들이 반영 된 html요소가 넘어감 -> DB에 기록 <br>
      html태그가 같이 섞여있는 구문을 뿌리면 적용 된 채로 보이게<br>
      > 에디터 api : summernote, ckeditor, 등등
   </p>
   
   <form action="${contextPath }/board/editor/insert.do" method="post">
      게시글 제목 : <input type="text" name="boardTitle"><br>
      게시글 내용 : <textarea id="summernote" name="boardContent"></textarea><br>
      <button type="submit">등록</button>
   </form>
   <!-- include libraries(jQuery, bootstrap) -->

<!-- include summernote css/js -->
   <script>
      $(document).ready(function() {
           $('#summernote').summernote({
              width: 512,
              height: 500,
              placeholder: 'hello world',
              callbacks: {
                 onImageUpload: function(images) {
                    //이미지 들어오는 순간 자동 실행
                    //비동기식으로 이미지 업로드
                    for(let i=0; i<images.length;i++){
                       
                       let formData = new FormData();
                       formData.append('image',images[i]);
                       
                       $.ajax({
                          url: '${contextPath}/board/editor/imageUpload.do',
                          type: 'post',
                          data: formData,
                          processData: false,
                          contentType: false,
                          async: false,//동기방식으로 처리하겠다.
                          success: function(src){
                             //파일 업로드 후에 저장경로+저장파일명을 응답데이터로 받아
                             //현재 에디터의 <img src속성값을 응답데이터로 변경>
                             $("#summernote").summernote('insertImage',"${contextPath}"+src);
                          }
                       })
                    }
                 }
              }
           });
         });  
   </script>  

     
</body>
</html>
 

 

 

- 첫번째는 오류, 두번째는 잘됨.

 

 

- callbacks 속성 추가.

- 이 함수는 이미지 하나가 들어오는 순간 자동으로 실행된다. 

그때 이미지 파일이 돌아온다. 받고 싶다면 매개변수를 둔다. image라는 매개변수를 뒀다.

돌아오는 이미지 파일은 하나일 수도 있고 여러개일 수도 있다.

- 돌아오는 형태는 배열이다. 반복문으로 돌린다.

 

- formData. 저 가상의 폼요소에 내가 넘기고자 하는 키밸류 세트를 담을 수 있다.

- 에디터에 ~ 파일만 넘기고 있다.

 

- ajax로 파일을 넘길때는 항상 post 방식이고, processData과 contentType 속성을 반드시 추가해야 한다.

 

- 파일이 10개면 ajax 요청이 10번이다.

그런데 ajax는 비동기 방식이다보니까 응답이 언제 돌아올지 모른다. 실행되는 순서가 뒤죽박죽이다.

그래서 async: false를 줘서 순차적으로 실행시킬 수 있다. 

연속적으로 실행되는 ajax를 순차적으로 실행시킨다.

 

 

- 아까 이미지의 src 속성값이 base64로 엄청 길었다. 그래서 하는 과정이다.

이미지의 src 속성값을 수정하고, 수정한 정보로 db에 기록하려고 하는 것이다.

- 즉 응답데이터로 돌려받아야 하는 거이 이미지의 src에 넣어줄 값이다.

 

 

- 컨트롤러에서 돌아온 응답데이터를 받고 싶다면 success function에 매개변수를 둬야 한다.

- $("summernote")

- $("summernote").summernote('insertImage', '${contextPath}' + src);

 

 

- 이건 필수는 아닌데 해두면 좋다. 

- 내가 원하는 경로에 저장하고 ~.

 

 

 

 

 

ㅁ BoardController

 
package com.br.file.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;
import com.br.file.service.BoardService;
import com.br.file.util.FileUtil;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequestMapping("/board")
@RequiredArgsConstructor
@Controller
public class BoardController {

  private final BoardService boardService;
  private final FileUtil fileUtil;
 
 
  @PostMapping("/insert1.do")
  public String insertOneFileBoard(BoardDto board, MultipartFile uploadFile) {
   
    log.debug("board: {}", board);      // 잘 담겼는지 확인용 로그 출력
    log.debug("attach: {}", uploadFile);  // 잘 담겼는지 확인용 로그 출력
   
   
    AttachDto attach = null;
   
    if(uploadFile != null && !uploadFile.isEmpty()) { // 첨부파일이 존재할 경우 => 업로드
      // 전달된 파일 업로드 처리
     

      // FileUitl 클래스의 fileupload 메소드를 호출 (uploadFile을 넘기면서)
      Map<String, String> map = fileUtil.fileupload(uploadFile);
     
     
      // (4) db에 기록할 정보를 자바 객체(AttachDto)에 세팅
      attach = AttachDto.builder()
                .filePath(map.get("filePath"))
                .originalName(map.get("originalFilename"))
                .filesystemName(map.get("filesystemName"))
                .build();
     
    }
   


    int result = boardService.insertOneFileBoard(board, attach);
   
    if(result > 0) {
      log.debug("게시글 등록 성공");
    }else {
      log.debug("게시글 등록 실패");
    }

    return "redirect:/"; // 성공이든 실패든 메인페이지 요청
   
  }
 
 
 
 
 
  @PostMapping("/insert2.do")
  public String insertManyFileBoard(BoardDto board, List<MultipartFile> uploadFile) {
   
    List<AttachDto> list = new ArrayList<>();
   
   
    for(MultipartFile file : uploadFile) {
     
      if(file != null && !file.isEmpty()) { // 파일이 존재할 경우
       
        Map<String, String> map = fileUtil.fileupload(file);
        list.add(AttachDto.builder()
                  .filePath(map.get("filePath"))
                  .originalName(map.get("originalFilename"))
                  .filesystemName(map.get("filesystemName"))
                  .build());
      }
    }
   
   
    // 넘어온 첨부파일이 없었다면 list는 비어있는 상태다.
    int result = boardService.insertManyFileBoard(board, list);
   
    // 첨부파일이 없었다면 result는 board에만 insert되고 result는 1이다.
    // 첨부파일이 있었다면 첨부파일의 개수만큼
    if(list.isEmpty() && result == 1 || !list.isEmpty() && result == list.size()) {
      log.debug("다중 첨부파일 게시글 등록 성공");
    }else {
      log.debug("다중 첨부파일 게시글 등록 실패");
    }
   
   
    return "redirect:/"; // 성공이든 실패든 메인페이지 요청
  }
 
 
  @ResponseBody
  @PostMapping("/ajaxInsert.do")
  public String insertAjaxFileBoard(BoardDto board, MultipartFile uploadFile) {
   
 
    AttachDto attach = null;
   
    if(uploadFile != null && !uploadFile.isEmpty()) {
     
      Map<String, String> map = fileUtil.fileupload(uploadFile);
     
      attach = AttachDto.builder()
                .filePath(map.get("filePath"))
                .originalName(map.get("originalFilename"))
                .filesystemName(map.get("filesystemName"))
                .build();
    }
   
   
    int result = boardService.insertOneFileBoard(board, attach);
   
    if(result > 0) {
      log.debug("ajax 첨부파일 게시글 등록 성공");
      return "SUCCESS";
    }else {
      log.debug("ajax 첨부파일 게시글 등록 실패");
      return "FAIL";
    }
   
  }
 
 
  @ResponseBody
  @GetMapping(value="/atlist.do", produces="application/json")
  public List<AttachDto> selectAttachList() {
   
    return boardService.selectAttachList(); // success 함수의 매개변수로 리턴된다.
  }
 
 
  @PostMapping("/editor/insert.do")
  public String editorInsertBoard(BoardDto board) {
   
    log.debug("board: {}", board);
    int result = boardService.insertOneFileBoard(board, null);
   
    if(result > 0) {
      log.debug("에디터 게시글 등록 성공");
    }else {
      log.debug("에디터 게시글 등록 실패");
    }
   
    return "redirect:/";
  }
 
 
  @ResponseBody
  @PostMapping("/editor/imageUpload.do")
  public String editorImageUpload(MultipartFile image) {
    Map<String, String> map = fileUtil.fileupload(image);
    return map.get("filePath") + "/" + map.get("filesystemName");
    //   /upload/20241025/~~~~~~~~~.jpg <- 이렇게 제작한 경로를 이미지의 src 속성값으로 부여할 예정이다.
  }
 
 
}
 

 

 

- Map을 돌려받아서 ~를 꺼낼 수 있다.

 

 

 

 

 

 

 

 

 

===================================================================================

 

 

 

ㅁ main.jsp

 

 
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<c:set var="contextPath" value="${pageContext.request.contextPath}"/>    
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<style>
   form>label {
      font-size: 12px;
      color: gray;
   }
</style>
</head>
<body>
   <script>/* $(function): 요소가 다 만들어지고 스크립트실행  */
      $(function(){
         $("input[type=file]").on('change',function(evt){
            const files = evt.target.files;//FileList타입 {0:File,1:File..}
            console.log(files);
           
            let totalSize = 0;                   //File전체용량
           
            for(let i=0;i<files.length;i++){//File객체에 접근
               if(files[i].size > 10 * 1024 * 1024){//하나의 파일 크기가 10메가 초과일 경우
                  alert("첨부 파일의 최대 크기는 10MB.");
                  evt.target.value="";
                  return;  
               }
               totalSize += files[i].size;      //누적해서 파일 용량 더해주기
               
               if(totalSize > 100 * 1024 * 1024){//전체 첨부파일 사이즈가 100메가 초과일 경우
                  alert("총 용량의 최대 크기는 100MB입니다.");
                  evt.target.value="";
                  return;
               }
               
            }//for
           
         })/* 이런 function: eventHandler */
      })
   </script>
   
   <h2>1. 한 개의 첨부파일 업로드 테스트</h2>
   <form action="${ contextPath }/board/insert1.do" method="post" enctype="multipart/form-data">
      <!-- 파일이 파일로 넘어가게 하려면 enctype작성필요 -->
      게시글 제목 : <input type="text" name="boardTitle"><br>
      게시글 내용 : <textarea name="boardContent"></textarea><br>
      첨부파일 : <input type="file" name="uploadFile"><br>
      <label>첨부 파일 사이즈 10MB 이하</label><br><br>
     
      <button type="submit">등록</button>
   </form>
   
   <h2>2. 다중 첨부파일 업로드 테스트</h2>
   <form action="${ contextPath }/board/insert2.do" method="post" enctype="multipart/form-data">
      게시글 제목 : <input type="text" name="boardTitle"><br>
      게시글 내용 : <textarea name="boardContent"></textarea><br>
      첨부파일 : <input type="file" name="uploadFile" multiple><br>
      <label>각 첨부 파일 사이즈는 10MB 이하, 총100MB이하여야 합니다.</label><br><br>
     
      <button type="submit">등록</button>
   </form>  
   
   <h2>3. 비동기식 첨부파일 업로드 테스트</h2>
   <div id="async_test">
      게시글 제목 : <input type="text" id="title"><br>
      게시글 내용 : <textarea id="content"></textarea><br>
      첨부파일 : <input type="file" id="file"><br><br>
     
      <button type="button" id="submit">등록</button>
   </div>
   
   <script>
      $(function(){
         $("#submit").on("click",function(){
           
            //ajax게시글 등록(요청시 전달값 : 제목,내용,파일)
            //가상의 form요소에 담아서 전달
            //첨부파일 전달 해야 할 경우->FormData객체(가상의 form요소)에 담아서 전달
            let formData = new FormData();//javaScript객체
            formData.append("boardTitle",document.getElementById("title").value);
            formData.append("boardContent",document.getElementById("content").value);
            formData.append("uploadFile",document.getElementById("file").files[0]);//File객체 담기. files[]
           
            $.ajax({
               url: '${contextPath}/board/ajaxInsert.do',
               type: 'post',
               data: formData,//요청시 전달값 이미 담아놨음
               //첨부파일 기능 있을 시 ㅍ평소와 다르게 아래 두가지 넣음
               processData: false,//false선언시
               contentType: false,
               success: function(result){
                  if(result == "SUCCESS"){
                     alert("등록 성공");
                     location.reload();//새로고침 진행
                  }else{                    
                     alert("등록 실패");
                  }
               },
               error: function(){
                  console.log()
               }
            })
           
         })
      })
   </script>
   
   
   <h2>4. 첨부파일 목록 조회(비동기식)</h2>
   <button onclick="fn_selectAttachList();">조회</button>
   <div id="result">
     
   </div>
   
   <script>
      function fn_selectAttachList(){
         $.ajax({
            url: '${contextPath}/board/atlist.do',
            success: function(resData){
               console.log(resData);
               
               let a = '';
               for(let i=0;i<resData.length;i++){
//                  a += '<a href=${contextPath}'+resData[i].filePath+'/'+resData[i].filesystemName+'" download="'+resData[i].originalName+'">'+resData[i].originalName + '</a><br>';
                  a += '<a href="${contextPath}' + resData[i].filePath + '/' + resData[i].filesystemName + '" download="' + resData[i].originalName + '">' + resData[i].originalName + '</a><br>'
                     
                 
                 
                 
               }
               
               $("#result").html(a);
               
            }
         })
      }
   </script>
   
   <hr>
   <h2>번외. 내용 작성란에 에디터 적용 시키기</h2>
   <p>
      에디터 기능 사용 시 내용 작성 시 폰트 수정, 정렬, 이미지 삽입 등등이 가능함<br>
      위의 과정 들이 반영 된 html요소가 넘어감 -> DB에 기록 <br>
      html태그가 같이 섞여있는 구문을 뿌리면 적용 된 채로 보이게<br>
      > 에디터 api : summernote, ckeditor, 등등
   </p>
   
   <form action="${contextPath }/board/editor/insert.do" method="post">
      게시글 제목 : <input type="text" name="boardTitle"><br>
      게시글 내용 : <textarea id="summernote" name="boardContent"></textarea><br>
      <button type="submit">등록</button>
   </form>
   <!-- include libraries(jQuery, bootstrap) -->

<!-- include summernote css/js -->
   <script>
      $(document).ready(function() {
           $('#summernote').summernote({
              width: 512,
              height: 500,
              placeholder: 'hello world',
              callbacks: {
                 onImageUpload: function(images) {
                    //이미지 들어오는 순간 자동 실행
                    //비동기식으로 이미지 업로드
                    for(let i=0; i<images.length;i++){
                       
                       let formData = new FormData();
                       formData.append('image',images[i]);
                       
                       $.ajax({
                          url: '${contextPath}/board/editor/imageUpload.do',
                          type: 'post',
                          data: formData,
                          processData: false,
                          contentType: false,
                          async: false,//동기방식으로 처리하겠다.
                          success: function(src){
                             //파일 업로드 후에 저장경로+저장파일명을 응답데이터로 받아
                             //현재 에디터의 <img src속성값을 응답데이터로 변경>
                             $("#summernote").summernote('insertImage',"${contextPath}"+src);
                          }
                       })
                    }
                 }
              }
           });
         });  
   </script>  
   
    <h2>번외. 에디터로 등록한 게시글 조회해보기</h2>
    <form id="editor_search">
      조회할 글번호 : <input type="text" name="boardNo">
      <button type="button" onclick="fn_selectBoard();">검색</button>
    </form>
   
    <h4>조회결과</h4>
   
    <b>제목 : </b> <span id="title-result"></span> <br>
    <b>내용</b>
    <div id="content-result"></div>
     
    <script>
      function fn_selectBoard(){
        $.ajax({
          url: '${contextPath}/board/editor/detail.do',
          type: 'get',
          data: $('#editor_search').serialize(), // "boardNo=xx"
          success: function(resData){ // {boardNo:xx, boardTitle:xx, boardContent:xx}
            $('#title-result').text(resData.boardTitle);
            $('#content-result').html(resData.boardContent);
          }
         
        })
      }
    </script>  
     
     
     
     
     
</body>
</html>
 

 

 

 

ㅁ BoardController

 

 
package com.br.file.controller;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;
import com.br.file.service.BoardService;
import com.br.file.util.FileUtil;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequestMapping("/board")
@RequiredArgsConstructor
@Controller
public class BoardController {

  private final BoardService boardService;
  private final FileUtil fileUtil;
 
 
  @PostMapping("/insert1.do")
  public String insertOneFileBoard(BoardDto board, MultipartFile uploadFile) {
   
    log.debug("board: {}", board);      // 잘 담겼는지 확인용 로그 출력
    log.debug("attach: {}", uploadFile);  // 잘 담겼는지 확인용 로그 출력
   
   
    AttachDto attach = null;
   
    if(uploadFile != null && !uploadFile.isEmpty()) { // 첨부파일이 존재할 경우 => 업로드
      // 전달된 파일 업로드 처리
     

      // FileUitl 클래스의 fileupload 메소드를 호출 (uploadFile을 넘기면서)
      Map<String, String> map = fileUtil.fileupload(uploadFile);
     
     
      // (4) db에 기록할 정보를 자바 객체(AttachDto)에 세팅
      attach = AttachDto.builder()
                .filePath(map.get("filePath"))
                .originalName(map.get("originalFilename"))
                .filesystemName(map.get("filesystemName"))
                .build();
     
    }
   


    int result = boardService.insertOneFileBoard(board, attach);
   
    if(result > 0) {
      log.debug("게시글 등록 성공");
    }else {
      log.debug("게시글 등록 실패");
    }

    return "redirect:/"; // 성공이든 실패든 메인페이지 요청
   
  }
 
 
 
 
 
  @PostMapping("/insert2.do")
  public String insertManyFileBoard(BoardDto board, List<MultipartFile> uploadFile) {
   
    List<AttachDto> list = new ArrayList<>();
   
   
    for(MultipartFile file : uploadFile) {
     
      if(file != null && !file.isEmpty()) { // 파일이 존재할 경우
       
        Map<String, String> map = fileUtil.fileupload(file);
        list.add(AttachDto.builder()
                  .filePath(map.get("filePath"))
                  .originalName(map.get("originalFilename"))
                  .filesystemName(map.get("filesystemName"))
                  .build());
      }
    }
   
   
    // 넘어온 첨부파일이 없었다면 list는 비어있는 상태다.
    int result = boardService.insertManyFileBoard(board, list);
   
    // 첨부파일이 없었다면 result는 board에만 insert되고 result는 1이다.
    // 첨부파일이 있었다면 첨부파일의 개수만큼
    if(list.isEmpty() && result == 1 || !list.isEmpty() && result == list.size()) {
      log.debug("다중 첨부파일 게시글 등록 성공");
    }else {
      log.debug("다중 첨부파일 게시글 등록 실패");
    }
   
   
    return "redirect:/"; // 성공이든 실패든 메인페이지 요청
  }
 
 
  @ResponseBody
  @PostMapping("/ajaxInsert.do")
  public String insertAjaxFileBoard(BoardDto board, MultipartFile uploadFile) {
   
 
    AttachDto attach = null;
   
    if(uploadFile != null && !uploadFile.isEmpty()) {
     
      Map<String, String> map = fileUtil.fileupload(uploadFile);
     
      attach = AttachDto.builder()
                .filePath(map.get("filePath"))
                .originalName(map.get("originalFilename"))
                .filesystemName(map.get("filesystemName"))
                .build();
    }
   
   
    int result = boardService.insertOneFileBoard(board, attach);
   
    if(result > 0) {
      log.debug("ajax 첨부파일 게시글 등록 성공");
      return "SUCCESS";
    }else {
      log.debug("ajax 첨부파일 게시글 등록 실패");
      return "FAIL";
    }
   
  }
 
 
  @ResponseBody
  @GetMapping(value="/atlist.do", produces="application/json")
  public List<AttachDto> selectAttachList() {
   
    return boardService.selectAttachList(); // success 함수의 매개변수로 리턴된다.
  }
 
 
  @PostMapping("/editor/insert.do")
  public String editorInsertBoard(BoardDto board) {
   
    log.debug("board: {}", board);
    int result = boardService.insertOneFileBoard(board, null);
   
    if(result > 0) {
      log.debug("에디터 게시글 등록 성공");
    }else {
      log.debug("에디터 게시글 등록 실패");
    }
   
    return "redirect:/";
  }
 
 
  @ResponseBody
  @PostMapping("/editor/imageUpload.do")
  public String editorImageUpload(MultipartFile image) {
    Map<String, String> map = fileUtil.fileupload(image);
    return map.get("filePath") + "/" + map.get("filesystemName");
    //   /upload/20241025/~~~~~~~~~.jpg <- 이렇게 제작한 경로를 이미지의 src 속성값으로 부여할 예정이다.
  }
 
  @ResponseBody
  @GetMapping(value = "/editor/detail.do", produces="application/json")
  public BoardDto editorSelectBoard(int boardNo) {
    BoardDto b = boardService.selectBoard(boardNo);
    return b;
  }
 
}
 

 

 

ㅁ BoardService

 

 
package com.br.file.service;

import java.util.List;

import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;

public interface BoardService {

  // 한 개의 첨부파일과 함께 게시글 등록
  int insertOneFileBoard(BoardDto board, AttachDto attach);
 
  // 다중 첨부파일과 함께 게시글 등록
  int insertManyFileBoard(BoardDto board, List<AttachDto> list);
 
  // 첨부파일 목록 조회
  List<AttachDto> selectAttachList();
 
 
  // 글번호로 게시글 조회
  BoardDto selectBoard(int boardNo);
 
}
 

 

 

 

ㅁ BoardServiceImpl

 

 
package com.br.file.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.br.file.dao.BoardDao;
import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class BoardServiceImpl implements BoardService {

  private final BoardDao boardDao;

  @Override
  public int insertOneFileBoard(BoardDto board, AttachDto attach) {
   
   
    int result = boardDao.insertBoard(board);
   
    if(result > 0 && attach != null) {
      result = boardDao.insertAttach(attach);
    }
   
   
    return result;
  }
 

  @Override
  public int insertManyFileBoard(BoardDto board, List<AttachDto> list) {
   
    int result = boardDao.insertBoard(board);
   
   
    if(result > 0 && !list.isEmpty()) {
      result = 0;
      for(AttachDto attach : list) {
        result += boardDao.insertAttach(attach);
      }
    }
   
    return result;
  }
 

  @Override
  public List<AttachDto> selectAttachList() {
    return boardDao.selectAttachList();
  }


  @Override
  public BoardDto selectBoard(int boardNo) {
    return boardDao.selectBoard(boardNo);
  }
 
}
 

 

 

 

 

ㅁ BoardDao

 

 
package com.br.file.dao;

import java.util.List;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;

import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Repository
public class BoardDao {

  private final SqlSessionTemplate sqlSession;
 
 
  public int insertBoard(BoardDto board) {
    return sqlSession.insert("boardMapper.insertBoard", board);
  }
 
  public int insertAttach(AttachDto attach) {
    return sqlSession.insert("boardMapper.insertAttach", attach);
  }
 
  public List<AttachDto> selectAttachList() {
    return sqlSession.selectList("boardMapper.selectAttachList");
  }
 
 
  public BoardDto selectBoard(int boardNo) {
    return sqlSession.selectOne("boardMapper.selectBoard", boardNo);
  }
 
}
 

 

 

 

ㅁ board-mapper.xml

 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="boardMapper">

  <resultMap id="attachResultMap" type="AttachDto">
    <result column="file_no" property="fileNo" />
    <result column="file_path" property="filePath" />
    <result column="original_name" property="originalName" />
    <result column="filesystem_name" property="filesystemName" />
    <result column="ref_board_no" property="refBoardNo" />
  </resultMap>

  <resultMap id="boardResultMap" type="BoardDto">
    <result column="board_no" property="boardNo" />
    <result column="board_title" property="boardTitle" />
    <result column="board_content" property="boardContent" />
  </resultMap>

 
  <insert id="insertBoard">
    insert
      into board
      (
        board_no
      , board_title
      , board_content
      )
      values
      (
        seq_bno.nextval
      , #{boardTitle}
      , #{boardContent}
      )
  </insert>
 
 
 
  <insert id="insertAttach">
    insert
      into attachment
      (
        file_no
      , file_path
      , original_name
      , filesystem_name
      , ref_board_no
      )
      values
      (
        seq_fno.nextval
      , #{filePath}
      , #{originalName}
      , #{filesystemName}
      , seq_bno.currval
      )
  </insert>
 
 
  <select id="selectAttachList" resultMap="attachResultMap">
    select
            file_no          
          , file_path        
          , original_name    
          , filesystem_name  
          , ref_board_no      
      from  attachment      
  </select>
 
 
  <select id="selectBoard" resultMap="boardResultMap">
    select
           board_no
         , board_title
         , board_content
      from board
     where board_no = #{boardNo}      
  </select>
 
</mapper>
 

 

 

- resultMap도 만든다.