본문 바로가기
05_Server (04. JSP 프로그래밍 구현)

[3-3] 일반게시판서비스 작성요청(파일업로드)

by moca7 2024. 9. 11.

 

 

ㅁ 첨부파일 업로드

 

 

ㅁ 일반게시글 목록페이지

 

 

 

 

- 로그인 상태면 '등록하기' 버튼이 보인다.

'등록하기' 버튼을 누르면 일반게시글 작성페이지로 간다.

 

 

 

 

 

ㅁ 일반게시글작성 페이지를 토대로 boardWrite.jsp를 만든다.

 

 
<%@ page import="java.util.List" %>
<%@ page import="com.br.web.board.model.vo.Category" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
 
<%
  List<Category> list = (List<Category>)request.getAttribute("list");
%>
 
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>

  <div class="container p-3">

    <!-- Header, Nav start -->
    <%@ include file="/views/common/header.jsp" %>
    <!-- Header, Nav end -->

    <!-- Section start -->
    <section class="row m-3" style="min-height: 500px;">
      <div class="container border m-4 p-5 rounded">

        <h2 class="m-4">일반게시글 작성</h2>
       
        <form action="<%= contextPath %>/insert.bo" method="post" enctype="multipart/form-data" class="m-4">
          <table class="table">
            <tr>
              <th width="100">카테고리</th>
              <td>
                <select name="category" class="form-control">
                  <% for(Category c : list) { %>
                  <option value="<%=c.getCategoryNo()%>"><%=c.getCategoryName()%></option>
                  <% } %>
                </select>
              </td>
            </tr>
            <tr>
              <th>제목</th>
              <td><input type="text" class="form-control" name="title" required></td>
            </tr>
            <tr>
              <th>내용</th>
              <td><textarea rows="10" class="form-control" name="content" style="resize:none;" required></textarea></td>
            </tr>
            <tr>
              <th>첨부파일</th>
              <td><input type="file" name="upfile"></td>
            </tr>
            <tr>
              <td colspan="2" align="center">
                <button type="submit" class="btn btn-outline-secondary btn-sm">등록하기</button>
                <button type="reset" class="btn btn-outline-danger btn-sm">초기화</button>
                <button type="button" class="btn btn-outline-warning btn-sm" onclick="history.back();">뒤로가기</button>
              </td>
            </tr>
          </table>
        </form>
       
      </div>
    </section>
    <!-- Section end -->

    <!-- Footer start -->
    <%@ include file="/views/common/footer.jsp" %>
    <!-- Footer end -->

  </div>

</body>
</html>
 

 

 

 

 

 

- '등록하기' 버튼을 눌러서 submit하면 /insert.bo라는 서블릿을 호출한다.

- 일반게시글 작성은 첨부파일 하나를 첨부할 수 있게 한다. 필수는 아니다.

나중에 사진게시글 작성은 첨부파일을 여러개 첨부한다.

 

- 첨부파일은 파일 자체를 넘긴다. 파일 자체를 넘겨서 서버에 보관(저장)을 시켜야 다른 사용자가 이 게시글을 봤을 때 다운로드 같은 기능을 할 수 있다.

 

- form에 새로운 속성을 사용한다. form submit시 파일 그 자체를 넘기고자 한다면 추가적으로 이 속성을 줘야 한다.

이 속성을 주지 않으면 파일 자체가 넘어가지 않고 파일명만 텍스트로 넘어간다.

- enctype이라는 속성의 값으로 "multipart/form-data"를 써줘야 form submit시 파일이 넘어간다.

그리고 무조건 post방식이어야 한다. 

 

 

 

 

 

ㅁ BoardInsertController

 

 
package com.br.web.board.controller;

import java.io.File;
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.br.web.board.model.service.BoardService;
import com.br.web.board.model.vo.Attachment;
import com.br.web.board.model.vo.Board;
import com.br.web.common.utils.MyFileRenamePolicy;
import com.br.web.member.model.vo.Member;
import com.oreilly.servlet.MultipartRequest;

/**
 * Servlet implementation class BoardInsertController
 */
@WebServlet("/insert.bo")
public class BoardInsertController extends HttpServlet {
 
    private static final long serialVersionUID = 1L;
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public BoardInsertController() {
        super();
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
   
        request.setCharacterEncoding("utf-8");
       
        // multipart/form-data로 전송할 경우
        // request로부터 파라미터를 바로 뽑을 수 없음!
       
        // 1. 전달된 파일 업로드(서버에 저장)
        /*
         * * 파일 업로드 처리를 위한 라이브러리 : cos.jar
         *
         *   HttpServletRequest => MultipartRequest 변환
         *   MulpartRequest multiRequest
         *      = new MultipartRequest(request, 저장시킬폴더의물리적인경로, 용량제한값, "UTF-8", 파일명수정해주는FileRenamePolicy객체);
         *
         */
 
       
        // 1_1) 저장시킬 폴더의 물리적인 경로 (String savePath)
        String savePath = request.getServletContext().getRealPath("/resources/board_upfiles/");
 
       
        // 1_2) 파일 용량 제한값 : 10mbyte (int maxSize는 byte단위다.)
        //      byte => kbyte => mbyte => gbyte => tbyte => ... (1024배)
        int maxSize = 10 * 1024 * 1024;
 
       
        /*
         * 1_3) 파일명 수정처리해주는 FileRenamePolicy 객체 세팅
         *
         *      * 파일명을 수정해서 저장해야되는 이유
         *        (1) 중복된 파일명이 존재할 수 있음
         *        (2) 원본명에 한글/특수문자/공백 이 포함되어있을 수 있음 => 서버에 문제 생김
         *  
         *      * 기본적으로 파일명 수정해주는 DefaultFileRenamePolicy 객체 제공
         *        => rename 작업 내용
         *           ㄴ 기존에 동일한 파일명이 존재할 경우
         *              파일명 뒤에 카운팅된 숫자를 붙여주기만 함 (특문, 한글, 공백 그대로임)
         *              ex) aaa.jpg, aaa1.jpg, aaa2.jpg
         *    
         *      * 나만의 com.br.web.common.utils.MyFileRenamePolicy 객체 만들기 (일반 클래스에 implements)
         *        => rename 작업 내용
         *           ㄴ 전달된 파일명을 "업로드된시간_랜덤숫자5자리.기존확장자"
         */
       
        MultipartRequest multiRequest
            = new MultipartRequest(request, savePath, maxSize, "UTF-8", new MyFileRenamePolicy());
 
 
 
       
        // 2. DB에 데이터 기록
        //    카테고리번호,제목,내용,작성자회원번호 => Board에 Insert
        //    첨부파일원본명,수정명(실제업로드된파일명),저장폴더경로 => Attachment에 Insert (넘어온 첨부파일이 있었을 경우)
 
       
        // 게시글 데이터 => Board 에 담기
        String boardTitle = multiRequest.getParameter("title");
        String boardContent = multiRequest.getParameter("content");
        String category = multiRequest.getParameter("category"); // "20"
       
        HttpSession session = request.getSession();
        int userNo = ((Member)session.getAttribute("loginUser")).getUserNo();
       
        Board b = new Board();
        b.setBoardTitle(boardTitle);
        b.setBoardContent(boardContent);
        b.setCategory(category);
        b.setBoardWriter(String.valueOf(userNo));
 
 
       
        // 첨부파일 데이터 => Attachment 담기
        Attachment at = null; // 넘어온 첨부파일이 있을 경우 => 생성
       
        // * multiRequest.getOriginalFileName("키") : 첨부파일이 있었을 경우 "원본명" | 없을 경우 null
        if(multiRequest.getOriginalFileName("upfile") != null) {
            at = new Attachment();
            at.setOriginName(multiRequest.getOriginalFileName("upfile"));
            at.setChangeName(multiRequest.getFilesystemName("upfile"));
            at.setFilePath("/resources/board_upfiles/");
        }
       
        // 서비스 요청
        int result = new BoardService().insertBoard(b, at);
 
 
 
       
        // 3. 응답
        if(result > 0) { // 성공 => 다시목록페이지 => alert메세지
            session.setAttribute("alertMsg", "성공적으로 게시글이 등록되었습니다.");
            response.sendRedirect(request.getContextPath() + "/list.bo");
        }else { // 실패 => 에러페이지 => 에러메세지
           
            // 첨부파일이 있었을 경우
            // 이미 업로드된 파일 => 더이상 쓸모없음 => 파일제거
            if(at != null) {
                new File(savePath + at.getChangeName()).delete();
            }
           
            request.setAttribute("msg", "게시글 등록 실패");
            request.getRequestDispatcher("/views/common/errorPage.jsp").forward(request, response);
           
        }
       
   
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

}
 

 

 

- url mapping값은 /insert.bo다.

- 파일이 넘어올 수도 있고 넘어오지 않을 수도 있다.

- 넘어온 데이터를 db에 기록하기 전에 파일을 먼저 서버에 저장(업로드)시켜야 한다.

 

 

 

 

- (1) 전달된 파일 업로드(서버에 저장) => (2) DB에 데이터 기록 => (3) 응답

 

- 전달된 파일을 어떤 폴더에 저장시킬건지 폴더를 먼저 만들어 놓는다.

- webapp폴더에 assets라는 폴더도 있지만 assets는 사이트에 필요한 정적 자원들을 보관시킬거고, 사용자가 저장시킨 파일들은 resources라는 일반 폴더를 만들어서 저장해본다.

- src/main/webapp/resources/board_upfiles 라는 폴더를 만들고 여기에 저장한다. 

 

 

 

 

- 이클립스에서는 외부 기능에 의해서 파일이 추가된 경우 실시간으로 안보여진다. 반드시 f5를 해야 저장된 파일을 볼 수 있다.

 

 

 

 

 


- 파일 업로드를 톰캣으로 할 수도 있지만 너무 복잡하다.

파일 업로드를 수월하게 해주는 외부 라이브러리들이 굉장히 많다. 

 

※ 파일 업로드 처리를 위한 라이브러리 : cos.jar

- 파일 업로드 관련한 클래스들이 잔뜩 모여있다. 

- 이걸 다운받을 수 있는 사이트가 지금은 사라졌다. 

 

- HttpServletRequest 타입의 객체를 MultipartRequest 변환한다. (cos 라이브러리에서 제공하는 클래스로)

멀티파트 리퀘스트 객체 생성 구문 한 줄로 파일이 업로드된다.

 

MulpartRequest multiRequest = new MultipartRequest(request, 저장시킬폴더의물리적인경로, 용량제한값, "UTF-8", 파일명수정해주는FileRenamePolicy객체);

 

 

 

 

- 요청시 전달값들은 request에 담겨있다. 파일이 있었다면 파일 데이터도 담겨있다. 
그러나 multipart/form-data로 전송한 경우에는 request로부터 파라미터를 바로 뽑을 수 없다.

 

- request에 담겨있는 파라미터들이 multiRequest 변수로 이동한다. 파일이 있었다면 이 과정에서 파일 업로드가 진행된다.

- 대신 이 파일을 어떤 폴더에 저장시킬건지, 용량 제한은 몇인지 등을 다 써줘야 한다.

- 마지막으로 "FileRenamePolicy객체"를 제시하면 이 객체가 내부적으로 작동되면서 파일명을 수정해준다.

 

 

 

 

 

- MultipartRequest는 cos.jar 라이브러리가 제공하는 클래스다.

- javax는 톰캣이 제공하는 클래스다.

- java는 순수 자바에서 제공하는 클래스다.

 

 

 

 

 

(1-1) 저장시킬 폴더의 물리적인 경로 (String savePath)

 

String savePath = request.getServletContext().getRealPath("/resources/board_upfiles/");

 

- 서블릿 컨텍스트 객체가 먼저 필요하다.

왜냐면 서블릿 컨텍스트 객체가 물리적인 경로를 알아내는 getRealPath라는 메소드를 제공하기 때문이다.

- 여기서의 슬래시는 루트(최상위 폴더)를 의미한다. 배포되는 폴더인 webapp폴더를 가리킨다.

- 중요한건 뒤에 슬래시까지 있어야한다. 이 폴더 안에 저장을 시킬것이기 때문에!!

 

 

 

(1-2) 파일 용량 제한값

- 파일 용량 제한값은 byte 단위로 환산해서 넣어줘야 한다.

 

 

 

(1-3) 파일명을 수정해주는 FileRenamePolicy 객체 세팅

- cos 라이브러리에서 기본적으로 파일명을 수정해주는 객체가 있다. 그걸 제시해도 된다. 근데 안쓴다.

DefaultFileRenamePolicy는 파일명 뒤에 숫자를 붙여주기만 해서 특수문자나 공백, 한글이 있는 경우 그대로 남아서 적절치 않다.

- 언더바는 허용되는 특수문자다. 

 

 
         *      * 파일명을 수정해서 저장해야되는 이유
         *        (1) 중복된 파일명이 존재할 수 있음
         *        (2) 원본명에 한글/특수문자/공백 이 포함되어있을 수 있음 => 서버에 문제 생김
         *  
         *      * 기본적으로 파일명 수정해주는 DefaultFileRenamePolicy 객체 제공
         *        => rename 작업 내용
         *           ㄴ 기존에 동일한 파일명이 존재할 경우
         *              파일명 뒤에 카운팅된 숫자를 붙여주기만 함
         *              ex) aaa.jpg, aaa1.jpg, aaa2.jpg
         *    
         *      * 나만의 com.br.web.common.utils.MyFileRenamePolicy 객체 만들기
         *        => rename 작업 내용
         *           ㄴ 전달된 파일명을 "업로드된시간_랜덤숫자5자리.기존확장자"
 

 

 

- com.br.web.common.utils 패키지를 만든다.

파일명을 수정해주는 도구여서 이런 것들은 utils 패키지를 만든다.

다른 게시판에서도 파일명 수정작업을 할 수 있기 때문에 common 패키지에 둔다. 

- 저 패키지에 일반 클래스로 만든다.

 

 

 

 

 

 
package com.br.web.common.utils;

import java.io.File;

import com.oreilly.servlet.multipart.FileRenamePolicy;

public class MyFileRenamePolicy implements FileRenamePolicy {
 

    @Override
    public File rename(File originFile) { // 내부적으로 해당 rename 메소드 호출시 원본파일 객체가 전달됨
       
        // 원본파일명("aaa.jpg") => 수정파일명("1702938174_12371.jpg")
        //                                        업로드시간(밀리세컨)_랜덤숫자5자리.확장자
       
        String originalFilename = originFile.getName(); // 원본파일명("aaa.jpg")
       
        // 파일명 수정 작업
        // 1. 파일업로드한 시간 (long currentTime)
        long currentTime = System.currentTimeMillis(); // 1970/01/01 부터 현재시간까지 경과한 시간을 밀리세컨초로 반환
        // 2. 랜덤숫자 5자리 (10000~99999) (int ranNum)
        int ranNum = (int)(Math.random() * 90000 + 10000);
        // 3. 원본파일의 확장자 (String ext)
        String ext = originalFilename.substring(originalFilename.lastIndexOf("."));
       
        String filesystemName = currentTime + "_" + ranNum + ext;
       
        return new File(originFile.getParent(), filesystemName);
    }
 

}
 

 

 

- 그냥 일반 클래스가 아니라, cos에서 제공하는 FileRenamePolicy라는 인터페이스를 구현하는 구현 클래스여야 MultipartRequest 객체 생성 구문의 마지막 인자값으로 제시할 수 있다.

 

- 인터페이스여서 추상메소드가 존재한다.

빨간줄에 마우스를 갖다대서 'Add unimplemented methods'를 누르면 내가 완성시켜야 하는 rename 메소드가 오버라이드 된다.

 

- MultipartRequest 객체 생성 구문 실행시 내부적으로 이 메소드가 자동으로 호출된다.

이 메소드 안에서 파일명을 수정해주는 코드를 작성하면 된다.

 

- rename 메소드에 매개변수로 원본 파일이 전달된다. 이해하기 쉽게 매개변수명을 originFile로 바꿨다.

원본 파일로부터 원본명, 파일 사이즈 등을 알아낼 수 있다. 

 

-  중복되지 않게끔 업로드한 시간을 밀리세컨 단위로 한다. 현재 시스템 시간이다.

밀리세컨초 단위는 숫자가 커서 long형에 담아야 한다.

 

- Java에서 숫자는 0으로 시작할 수 없기 때문에, 랜덤 숫자를 5자리로 만들고, 만약 자릿수가 모자라면 0을 앞에 붙여주는 작업을 추가로 해야 합니다.

 

- 보통 서버에 저장되는 파일명은 filesystemName이라고 한다. 

 

- new File의 첫번째 인자값으로는 원본 파일의 상위 폴더를 제시한다.

두번째 인자값으로는 어떤 이름으로 바꿀건지 제시한다. 

 

 

 

 

 

 

- 마지막으로 db에 데이터를 기록하기 전에 업로드가 우선 잘 되는지부터 확인한다.

서버 재시작하고 브라우저 새로고침, 로그인, 일반게시글을 첨부파일을 첨부해서 작성한다.

- 이클립스에서 src/main/webapp/resources/board_upfiles를 새로고침하면 방금 첨부한 파일이 rename한 파일명으로 보인다.

 

 

 

 

 

(2)  DB에 데이터 기록

 
        // 2. DB에 데이터 기록 
        //    카테고리번호,제목,내용,작성자회원번호 => Board에 Insert
        //    첨부파일원본명,수정명(실제업로드된파일명),저장폴더경로 => Attachment에 Insert (넘어온 첨부파일이 있었을 경우)
 
       
        // 게시글 데이터 => Board 에 담기
        String boardTitle = multiRequest.getParameter("title");
        String boardContent = multiRequest.getParameter("content");
        String category = multiRequest.getParameter("category"); // "20"
       
        HttpSession session = request.getSession();
        int userNo = ((Member)session.getAttribute("loginUser")).getUserNo();
       
        Board b = new Board();
        b.setBoardTitle(boardTitle);
        b.setBoardContent(boardContent);
        b.setCategory(category);
        b.setBoardWriter(String.valueOf(userNo));
 
       
        // 첨부파일 데이터 => Attachment 담기
        Attachment at = null; // 넘어온 첨부파일이 있을 경우 => 생성
       
        // * multiRequest.getOriginalFileName("input 태그의 key값") : 첨부파일이 있었을 경우 "원본명" | 없을 경우 null
        if(multiRequest.getOriginalFileName("upfile") != null) {
            at = new Attachment();
            at.setOriginName(multiRequest.getOriginalFileName("upfile"));
            at.setChangeName(multiRequest.getFilesystemName("upfile"));
            at.setFilePath("/resources/board_upfiles/");
        }
       
        // 서비스 요청
        int result = new BoardService().insertBoard(b, at);
 

 

 

- 요청시 넘어온 전달값들은 처음에는 request 객체에 있었겠지만 지금은 multiRequest 객체에서 뽑는다.

- 2개의 테이블에 insert하지만 하나의 서비스 메소드에서 다 진행한다. 결국 게시글 작성이라는 하나의 기능이다.

실행시킬 쿼리는 2개이므로 2개의 dao 메소드로 나눠서 실행한다.

 

- 첨부파일이 넘어왔는지 안넘어왔는지 비교하기 위해 일단 NULL로 초기화해놓는다.

- 첨부파일에 대한 정보도 multiRequest 객체에 담겨 있다.

- multiRequest.getOriginalFileName()에 input 태그의 key값을 제시하면 첨부파일에 대한 정보를 알아낼 수 있다.

첨부파일이 있었을 경우 문자열로 원본명을 반환한다. 

- multiRequest.getFilesystemName()에 input 태그의 key값을 제시하면 넘어온 파일이 실제로 어떤 이름으로 저장되었는지를 반환한다.

- 이 파일이 저장된 폴더 경로는  C드라이브부터의 물리적인 경로를 담아서는 안 된다. 

아까 저장시켰던 일부 경로를 제시한다. 이걸 db에 기록한다.

 

- 두 개의 insert문을 하나의 서비스로 묶어서 진행한다. 하나의 기능이니까.

 

 

 

 

 
    public int insertBoard(Board b, Attachment at) {
 
        Connection conn = getConnection();
       
        // 1) Board에 Insert
        int result = bDao.insertBoard(conn, b);
       
        if(result > 0 && at != null) {
            // 2) Attachment에 Insert
            result = bDao.insertAttachment(conn, at);
        }
       
        if(result > 0) {
            commit(conn);
        }else {
            rollback(conn);
        }
 
        close(conn);
        return result;
       
    }
 

 

 

- Attachment 테이블은 참조 게시글 번호 컬럼이 있기 때문에 부모 테이블인 Board에 먼저 insert 해야 한다.

- Attachment 테이블에 insert는 그냥 하면 안된다.

Board에 insert가 성공해야만 하고, 또 첨부파일이 있는 경우에만 해야 한다.

- 둘 중 하나라도 잘 안된 경우에는 전부 rollback을 해야 한다. 

 

 

 

 

 

※ 시퀀스 현재값을 가져오는 방법

 

 

- 시퀀스.CURRVAL하면 위에서 INSERT문으로 마지막으로 수행된 NEXTVAL의 값(현재값)을 알아낼 수 있다. 

 

 

 

 

 

 

(3) 응답

 

 
        // 3. 응답
        if(result > 0) { // 성공 => 다시목록페이지 => alert메세지
            session.setAttribute("alertMsg", "성공적으로 게시글이 등록되었습니다.");
            response.sendRedirect(request.getContextPath() + "/list.bo");
        }else { // 실패 => 에러페이지 => 에러메세지
           
            // 첨부파일이 있었을 경우
            // 이미 업로드된 파일 => 더이상 쓸모없음 => 파일제거
            if(at != null) {
                new File(savePath + at.getChangeName()).delete();
            }
           
            request.setAttribute("msg", "게시글 등록 실패");
            request.getRequestDispatcher("/views/common/errorPage.jsp").forward(request, response);
           
        }
 

 

 

- redirect할 때 "/list.bo?page=n" 이렇게 키밸류 세트로 넘겨도 된다. 페이징처리 되어있어서.

"/list.bo"만 쓰면 1번 페이지 요청으로 간주된다. 

 

- db에 기록이 실패했지만, 이미 그전에 첨부파일이 있었을 경우에는 이미 업로드가 되었다. 삭제해야 한다.

 

- 파일 제거는 삭제하고자하는 파일을 파일 객체로 생성해서 delete() 메소드만 호출하면 된다.

대신 그 파일을 선택해야 한다. 그 파일이 저장된 물리적인 경로 안에 실제로 저장된 파일명을 제시해서 파일 객체로 생성시킨다. 

 

 

 

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

 

 

ㅁ 문제

 

 

(1) com.test.common.MyFileRenamePolicy

   
    @Override
    public File rename(File arg0) {
       
        // 파일명 수정작업하는 구문 작성하기.
       
        //  파일명은 "test_" + "년월일시분초" + 랜덤5자리 + 확장자가 되도록 할 것.
        // "저장경로 + 수정명" 하나의 문자열로 db에 file_url 컬럼에 보관할 것.
       
        String originalFilename = arg0.getName(); // 원본파일명
       
        int ranNum = (int)(Math.random() * 90000 + 10000); // 랜덤숫자 5자리 10000~99999
       
        String ext = originalFilename.substring(originalFilename.lastIndexOf(".")); // 확장자
       
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        String upTime= sdf.format(new Date()); // 년월일시분초
       
        String filesystemName = "test_" + upTime + ranNum + ext;
       
        return new File(arg0.getParent(), filesystemName);

    }
 

 

- 년월일시분초 형식을 위해 SimpleDateFormat 활용.

 

 

 

 

(2) com.test.board.controller.BoardInsertController

 
package com.test.board.controller;

import java.io.File;
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.oreilly.servlet.MultipartRequest;
import com.test.board.model.service.BoardService;
import com.test.board.model.vo.TestBoard;
import com.test.common.MyFileRenamePolicy;

@WebServlet("/insert.bo")
public class BoardInsertController extends HttpServlet {
    private static final long serialVersionUID = 1L;
       
    public BoardInsertController() {
        super();
        // TODO Auto-generated constructor stub
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       
        // 게시글 작성 요청시 실행해야되는 구문 작성하기.
       
        // 넘어오는 첨부파일 "resources/upload_files" 폴더에 업로드 시키기
        // 이때 파일명 수정 작업해주는 MyFileRenamePolicy 클래스가서 코드 작성하기
            // 수정명은 앞에 "test_" 붙이고 그 뒤에 "xxxxxxxx"(년월일시분초), xxxxx(랜덤숫자5자리) 붙이도록 할 것

        request.setCharacterEncoding("UTF-8");
       
        String savePath = request.getServletContext().getRealPath("/resources/upload_files/");
       
        int maxSize = 10 * 1024 * 1024;
       
        MultipartRequest multiRequest
        = new MultipartRequest(request, savePath, maxSize, "UTF-8", new MyFileRenamePolicy());
       
       
        // 카테고리, 게시글제목, 게시글내용, 첨부파일원본명, 첨부파일경로(폴더경로+수정명 합쳐서 fileURL) 뽑아서 db에 insert 시키기 (Service쪽에 insertBoard 메소드 정의되어있음)

        String category = multiRequest.getParameter("category");
        String boardTitle = multiRequest.getParameter("title");
        String boardContent = multiRequest.getParameter("content");
       
        TestBoard b = new TestBoard();
        b.setCategory(category);
        b.setBoardTitle(boardTitle);
        b.setBoardContent(boardContent);
        b.setFileOriginName(multiRequest.getOriginalFileName("upfile"));
        // b.setFileURL(savePath + multiRequest.getFilesystemName("upfile")); c부터 시작하는 경로 아님!
        b.setFileURL("/resources/upload_files/" + multiRequest.getFilesystemName("upfile"));
       
        int result = new BoardService().insertBoard(b);
        //System.out.println("result: " + result);
       
        // 작성 성공시 다시 게시글 목록페이지가 보여지도록 할 것
       
        if(result > 0) {
            response.sendRedirect(request.getContextPath() + "/list.bo");
        }else {
           
            // 첨부파일이 있었을 경우
            // 이미 업로드된 파일 => 더이상 쓸모없음 => 파일제거
            String filePath = savePath + multiRequest.getFilesystemName("upfile"); // 삭제할땐 savePath부터 시작하는 경로 맞음.
            File failedFile = new File(filePath);
           
            // 파일이 존재하면 삭제
            if(failedFile.exists()) {
                failedFile.delete();
            }
           
            request.setAttribute("msg", "게시글 등록 실패");
            request.getRequestDispatcher("/views/common/errorPage.jsp").forward(request, response);
           
        }
    }

   
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub
        doGet(request, response);
    }

}

 

 

- 첨부파일 테이블이 따로 있지 않고, 한 테이블에 첨부파일관련 컬럼이 있다. 

- 첨부파일 경로를 db에 기록할 때, "폴더경로" + "수정명"을 합친다.

이때 'savePath + multiRequest.getFilesystemName("upfile")'는 c드라이브부터 시작하는 경로다.

이게 아니라, "/resources/upload_files/" + multiRequest.getFilesystemName("upfile") 이 경로를 사용한다.