스프링 첨부파일 업로드

2022. 1. 24. 22:12Framework

반응형

첨부파일이 넘어올 경우 두 단계를 거쳐

1서버의 어떤 폴더에 직접적으로 저장

2db에도 저장

 

파일 자체를 넘길 시 enctype을 지정해야 함

enctype="multipart/form-data"

지정하지 않으면 파일명 즉 문자열만 넘어감

 

 

먼저 mvn리파지토리에서

commons-fileupload검색 후

Apache Commons FileUpload와

Apache Commons IO 디펜던시 복사

 

pom.xml에 디벤던시즈 안에 붙여넣기

 

스프링에서 제공하는 클래스라서 직접 수정 불가

어노테이션등록이 불가함으로 xml으로 등록

용량제한값을 무제한으로 줄 수 있는데

100메가로 설정

maxUploadSize, maxInMemorySize를 작성해서

용량제한값 설정. 밸류에는 바이트크기로 작성.

<!-- 파일업로드 관련 빈 등록 -->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
    <property name="maxUploadSize" value="100000000" />
    <property name="maxInMemorySize" value="100000000" />
</bean>

 

@vo

@NoArgsConstructor
@Setter 
@Getter
@ToString
public class Board {
	
	private int boardNo;
	private String boardTitle;
	private String boardWriter;
	private String boardContent;
	private String originName;
	private String changeName; // "resources/uploadFiles/xxx.jpg" 파일경로
	private int count;
	private String createDate;
	private String status;
}

 

 

@Controller

// ** 만약 다중파일 업로드 시?
//    여러개의 input type="file"요소에 동일한 키값으로 부여 (ex. name="upfile")
//    MultipartFile[] upfile로 전달받기. (0번 인덱스부터 차곡차곡 첨부파일이 담김)
//    반복문 돌려가면서 파일명 수정 작업 후 서버 업로드 작업
//    이때 Board테이블에 첨부파일을 같이 보관할 수 없음. 하나의 게시글에 여러 개의 첨부파일일 경우 첨부파일 별도 보관하는 테이블 생성
@RequestMapping("insert.bo")
public String insertBoard(Board b, MultipartFile upfile, HttpSession session, Model model) {
    // System.out.println(b);
    // System.out.println(upfile); // 첨부파일이 있든 없든 생성된 객체(다만 filename에 원본명이 있냐?)

    // 전달된 파일이 있을 경우 => 파일명 수정 작업 후 서버 업로드 => 원본명, 서버업로드된 경로를 b에 담기
    if(!upfile.getOriginalFilename().equals("")) {

        // 파일명 수정 작업 후 서버에 업로드 ("flower.png" => "20220118103646521.png")
        /*
        String originName = upfile.getOriginalFilename(); // "flower.png"

        String currentTime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); // "202201181036"
        int ranNum = (int)(Math.random() * 90000 + 10000); // 46521 (5자리랜덤값)
        String ext = originName.substring(originName.lastIndexOf(".")); // ".png"

        String changeName = currentTime + ranNum + ext;

        // 업로드한 파일을 보관할 폴더의 물리적인 경로 알아내어 파일을 changeName의 이름으로 저장
        // 물리적인 경로는 세션객체를 먼저 얻어내서 겟리얼패스메소드까지 호출
        String savePath = session.getServletContext().getRealPath("/resources/uploadFiles/");

        try {
            upfile.transferTo(new File(savePath + changeName));
        } catch (IllegalStateException | IOException e) {
            e.printStackTrace();
        }
        */

        String changeName = saveFile(upfile, session);
        // 세이브파일메소드를 업파일과 세션을 전달해서 호출. 리턴된 것을 체인지네임에 담기

        // 원본명, 서버업로드된경로를 Board b에 이어서 담기
        b.setOriginName(upfile.getOriginalFilename());
        b.setChangeName("resources/uploadFiles/" + changeName); // 어떤 경로에 저장되었는지도 같이 담기
    }

    // 넘어온 첨부파일 있을 경우 b : 제목, 작성자, 내용, 파일원본명, 파일저장경로
    // 넘어온 첨부파일 없을 경우 b : 제목, 작성자, 내용
    // 각 필드에 담긴 내용 이제 db에 인설트하러
    int result = bService.insertBoard(b);

    if(result > 0) {
        session.setAttribute("alertMsg", "게시글 등록 성공");
        return "redirect:list.bo";
    } else { 
        model.addAttribute("errorMsg", "게시글 등록 실패");
        return "common/errorPage";
    }
}

 

// 넘어온 첨부파일을 파일명 수정 작업 후 보관폴더에 업로드해주는 메소드
public String saveFile(MultipartFile upfile, HttpSession session) {
    String originName = upfile.getOriginalFilename(); // "flower.png"

    String currentTime = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); // "202201181036"
    int ranNum = (int)(Math.random() * 90000 + 10000); // 46521 (5자리랜덤값) 10000부터 90000개 즉 10000~99999 중 랜덤값
    String ext = originName.substring(originName.lastIndexOf(".")); // ".png" 뒤에서부터 .기준으로 문자열찾기 => 확장자 알아내는 법

    String changeName = currentTime + ranNum + ext; // 어떤 파일명으로 보관할지 세팅완성

    // 업로드한 파일을 보관할 폴더의 물리적인 경로 알아내어 파일을 changeName의 이름으로 저장
    // 물리적인 경로는 세션객체로부터 겟리얼패스메소드(jsp스코프중에 애플리케이션객체 얻어내기) 호출
    // 겟리얼패스메소드(보관할 폴더의 물리적인 경로 작성)
    String savePath = session.getServletContext().getRealPath("/resources/uploadFiles/");

    try {
        upfile.transferTo(new File(savePath + changeName));
        // 트랜스펄투: 파일객체의 경로와 체인지네임으로 실제 업로드하기 위한 메소드
    } catch (IllegalStateException | IOException e) {
        e.printStackTrace();
    }

    return changeName;

}

 

@RequestMapping("delete.bo")
public String deleteBoard(int bno, String filePath, HttpSession session, Model model) {
    // String filePath : boardDetailView.jsp상에 form태그 안에 filePath라는 키값으로 changeName(파일경로)밸류값을 넘긴 것을 받음
    // filePath : "resource/xxxx/xxxxx.jpg"

    int result = bService.deleteBoard(bno);

    if(result > 0) {

        // 첨부파일이 있을 경우 = > 서버에 업로드된 파일 삭제
        if(!filePath.equals("")) {
            new File(session.getServletContext().getRealPath(filePath)).delete();
            // 파일을 삭제하려면 파일이 저장되어있는 물리적인 경로가 필요함. 먼저 세션객체 필요
            // 세션객체로부터 겟서블릿컨텍스트메소드를 이용하면 jsp스코프중에 애플리케이션내장객체를 얻을 수 있고
            // 겟리얼패스메소드를 이용하면 물리적인 경로를 얻애낼 수 있음. 
            // 물리적인경로를 알아고자하는 일부경로를 괄호안에 제시하면 파일경로를 알아낼 수 있음.
            // 일부경로인 filePath를 제시하면 C드라이브에서부터 그 경로를 따라가서 파일을 찾음
            // 뉴파일. 찾아진 파일을 파일객체로 생성해서. 파일객체에서 제공하는 딜리트메소드를 호출하면 그 파일이 삭제됨
        }

        session.setAttribute("alertMsg", "게시글 삭제 성공");
        return "redirect:list.bo";
    } else {
        model.addAttribute("errorMsg", "게시글 삭제 실패");
        return "common/errorPage";
    }
}

 

@RequestMapping("update.bo")
public String updateBoard(Board b, MultipartFile reupfile, HttpSession session, Model model) {

    // 새로 넘어온 첨부파일 있을 경우
    if(!reupfile.getOriginalFilename().equals("")) {

        // 기존에 첨부파일이 있었을 경우 => 기존 첨부파일 삭제
        if(b.getOriginName() != null) {
            new File(session.getServletContext().getRealPath(b.getChangeName())).delete();
            // 세션객체로부터 겟서블릿컨텍스트메소드를 이용하면 jsp스코프중에 애플리케이션내장객체를 얻을 수 있고
            // 겟리얼패스메소드를 이용하면 물리적인 경로를 얻애낼 수 있음. 
            // 물리적인경로를 알아고자하는 일부경로를 괄호안에 제시하면 파일경로를 알아낼 수 있음.
            // 뉴파일. 찾아진 파일을 파일객체로 생성해서. 파일객체에서 제공하는 딜리트메소드를 호출하면 그 파일이 삭제됨
        }
        // 새로 넘어온 첨부파일 서버 업로드
        // originName을 changeName으로 변경하면서 업로드하기위해 saveFile메소드호출
        String changeName = saveFile(reupfile, session);
        // b에 새로 넘어온 첨부파일에 대한 원본명, 저장경로 담기
        b.setOriginName(reupfile.getOriginalFilename());
        b.setChangeName("resources/uploadFiles/" + changeName);
    }

    /*
     * b에 boardNo, boardTitle, boardContent는 무조건 담겨있는데
     * 
     * 1. 새로 첨부된 파일 x, 기존 첨부 파일 x
     * 	  => originName : null, changeName : null
     * 
     * 2. 새로 첨부된 파일 x, 기존 첨부 파일 o
     *    => originName : 기존첨부파일원본명, changeName : 기존첨부파일저장경로
     *    
     * 3. 새로 첨부된 파일 o, 기존 첨부 파일 x
     *    => 새로 전달된 파일 서버에 업로드
     *    => originName : 새로운첨부파일원본명, changeName : 새로운첨부파일저장경로
     *    
     * 4. 새로 첨부된 파일 o, 기존 첨부 파일 o
     *    => 기존의 파일 삭제
     *    => 새로 전달된 파일 서버에 업로드
     *    => originName : 새로운첨부파일원본명, changeName : 새로운첨부파일저장경로
     */
    int result = bService.updateBoard(b);

    if(result > 0) {
        session.setAttribute("alertMsg", "게시글 수정 성공");
        return "redirect:detail.bo?bno=" + b.getBoardNo();
    } else {
        model.addAttribute("alertMsg", "게시글 수정 실패");
        return "common/errorPage";
    }
}

 

@boardDetailView

<c:if test="${ loginUser.userId eq b.boardWriter }">
    <div align="center">
        <!-- 이렇게 기술하는 경우 get방식으로 url에 노출됨 
        	=> 의도치않은 수정,삭제가 발생할 가능성  
        <a class="btn btn-primary" href="updateForm.bo?bno=${ b.boardNo }">수정하기</a>
        <a class="btn btn-danger" href="delete.bo?bno=${ b.boardNo }">삭제하기</a>
        -->
        <a class="btn btn-primary" onclick="postFormSubmit(1)">수정하기</a>
        <a class="btn btn-danger" onclick="postFormSubmit(2)">삭제하기</a>
    </div><br><br>

    <form id="postForm" action="" method="post">
        <input type="hidden" name="bno" value="${ b.boardNo }">
        <input type="hidden" name="filePath" value="${ b.changeName }">
    </form>

    <script>
        function postFormSubmit(num) {
            if(num == 1) {
                $("#postForm").attr("action", "updateForm.bo").submit();
            } else {
                $("#postForm").attr("action", "delete.bo").submit();
            }
        }
    </script>
</c:if>

 

@boardUpdateForm

<c:if test="${ !empty b.originName }">
    현재 업로드된 파일 : 
    <a href="${ b.changeName }" download="${ b.originName }">${ b.originName }</a>
    <input type="hidden" name="originName" value="${ b.originName }">
    <input type="hidden" name="changeName" value="${ b.changeName }">
</c:if>

 

 

반응형