JSP

[JSP] File Update, Delete

shb 2022. 4. 12. 12:10

글 삭제 시 첨부파일 삭제 
‘글 수정’ 에 앞서 ‘글 삭제’ 부터 만들어 두면 좋다.
글 삭제시 두가지를 삭제 한다
1. 물리적으로 저장된 파일
2. DB 에 저장된 첨부파일 정보
 - 이는 DDL 에서 ON DELETE CASCADE 로 설정해도 처리 된다. (Lec_DBMS #09강 Constraint)

* FileDAO.java 에 추가

	// List<FileDTO> 의 물리적인 파일(들) 삭제
	public int deleteFiles(List<FileDTO> list, HttpServletRequest request) {
		if(list == null || list.size() == 0 || request == null) return 0;
		
		int cnt = 0;
		
		// 물리적인 경로
		ServletContext context = request.getServletContext();
		String saveDirectory = context.getRealPath("/upload");  // 실제 서버에 저장된 물리적인 경로
		
		for(FileDTO dto : list) {
			File f = new File(saveDirectory, dto.getFile());   // 물리적으로 저장된 파일들이 삭제 대상
			System.out.println("삭제시도--> " + f.getAbsolutePath());
			
			if(f.exists()) {
				if(f.delete()) {  // 삭제 성공
					System.out.println("삭제 성공");
					cnt++;
				} else {
					System.out.println("삭제 실패");
				}
			} else {
				System.out.println("파일이 존재하지 않습니다");
			}
		}
		
		return cnt;
	} // end deleteFiles()
	
	// 특정 글(write uid) 의 첨부파일(들) 삭제
	// 1) 물리적인 파일(들) 삭제, 2)DB 삭제
	public int deleteByWrUid(int wrUid, HttpServletRequest request) throws SQLException {
		int cnt = 0;
		
		try {
			// 특정 글(wrUid) 의 첨부파일들 정보 가져오기 
			pstmt = conn.prepareStatement(D.SQL_FILE_SELECT_BY_WRUID);
			pstmt.setInt(1, wrUid);
			rs = pstmt.executeQuery();
			
			List<FileDTO> list = buildList(rs);
			
			// 물리적인 파일(들) 삭제
			cnt = deleteFiles(list, request);
			
			// ※ test_file 에서 외래키 ON DELETE CASCADE 적용되어있으면 DB는 자동 삭제

			System.out.println("첨부파일 " + cnt + "개 삭제");
			
		} finally {
			close();
		}
		
		return cnt;
	} // end deleteByWrUid()
	
	
	// 복수개의 첨부파일(들) 제거
	// 글 '수정' 단계에서 발생.  '글' 이 삭제되는 것이 아니라서, 파일(들)만 개별적으로 삭제해야 함.
	public int deleteByUid(int [] uids, HttpServletRequest request) throws SQLException {
		
		if(uids == null || uids.length == 0 || request == null) return 0;  // 매개변수 검증
		
		int cnt = 0;
		
		// 101, 204, 319 번 파일을 읽어오려면
		// SELECT * FROM t2_file WHERE uid = 101 OR uid = 204 OR uid = 319
		// SELECT * FROM t2_file WHERE uid IN (101, 204, 319)
		
		// 101, 204, 319 번 파일을 지우려면? 
		// DELETE FROM t2_file WHERE uid = 101 OR uid = 204 OR uid = 319
		// DELETE FROM t2_file WHERE uid IN (101, 204, 319)
		
		try {
			// 삭제할 파일 정보들 읽어오기 -> List<FileDTO>
			StringBuffer sql = new StringBuffer("SELECT uid \"uid\", source \"source\", file \"file\" FROM t2_file WHERE uid IN (");
			for(int uid : uids) {
				sql.append(uid + ",");
			}
			sql.deleteCharAt(sql.lastIndexOf(","));  // 맨 끝의 콤마 삭제
			sql.append(")");
			
			
			stmt = conn.createStatement();
			rs = stmt.executeQuery(sql.toString());
			List<FileDTO> list = buildList(rs);
			
			// 1. 물리적인 파일(들) 제거
			deleteFiles(list, request);   // 파일 삭제
			
			// 2. file 테이블 삭제. '글' 이 삭제되는 것이 아니라서,  file 데이터를 개별적으로 삭제해야함.
			sql = new StringBuffer("DELETE FROM t2_file WHERE uid IN (");
			for(int uid : uids) {
				sql.append(uid + ",");
			}
	        sql.deleteCharAt(sql.lastIndexOf(","));  // 맨 끝의 콤마 삭제
		    sql.append(")");
		    
		    System.out.println("파일 테이블 삭제: " + sql);
		    cnt = stmt.executeUpdate(sql.toString());
		    
			System.out.println(cnt + " 개 삭제");
		    
		} finally {
			close();
		}
		
		return cnt;
		
	} // end deleteByUid()

* DeleteCommand.java

package command.write;

import java.sql.SQLException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import command.Command;
import domain.FileDAO;
import domain.WriteDAO;

public class DeleteCommand implements Command {

	@Override
	public void execute(HttpServletRequest request, HttpServletResponse response) {
		//입력한 값을 받아오기
		int uid = Integer.parseInt(request.getParameter("uid"));

		int cnt = 0;
		WriteDAO dao = new WriteDAO();
		FileDAO fileDao = new FileDAO();
		
		try {		
			// 첨부파일 먼저 삭제
			fileDao.deleteByWrUid(uid, request);
			
			// 글 삭제
			cnt = dao.deleteByUid(uid);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		request.setAttribute("result", cnt);
	}

}

수정
1. 기존 첨부된 파일은 오로지 ‘삭제’만 가능.
- 삭제될 t2_file.uid 값(들) 이 서버에 전달되어야 한다.

2. 새로운 첨부 파일 추가 가능.
- update 조차 multipart request 로 진행해야 한다.

 

* SelectCommand.java 수정

		// 첨부파일용 DAO 생성
		FileDAO fileDao = new FileDAO();
		List<FileDTO> fileList = null;
		
		// 첨부파일 DB 읽어들이기
		try {
			if(list != null && list.size() == 1) {  // 특정 글 1개에 대한 첨부파일
				fileList = fileDao.selectFilesByWrUid(uid);  // 첨부파일 DB 읽어오기				
				// 이미지 파일 여부 세팅 불필요				
				request.setAttribute("fileList", fileList); 
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} 
		

	}

* update.jsp 수정

1. jQuery 추가
2. <form> 에 enctype="Multipart/form-data" 추가
3. 첨부파일 목록 보여주기  ← 삭제할수 있도록 세팅
- 삭제 선택된 첨부파일(들)의  bf_uid 값(들)이 서버로 전달되어야 한다.
4. 새로운 첨부파일 추가 기능
- write.jsp 에서 작성한 내용 그대로 가져와도 된다.

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>    
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
    
<c:choose>
	<c:when test="${empty list || fn:length(list) == 0 }">
		<script>
			alert("해당 정보가 삭제되거나 없습니다");
			history.back();
		</script>
	</c:when>
	
	<c:otherwise>
		<c:set var="dto" value="${list[0] }"/>


<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.1/dist/js/bootstrap.bundle.min.js"></script>
	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

    <%-- Summernote 웹에디터--%>
    <link href="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/summernote@0.8.18/dist/summernote-lite.min.js"></script>


    <title>수정 -  ${dto.subject }</title>
</head>

<script>
$(document).ready(function(){
	$('#content').summernote({
		height: 300
	});
});
</script>

<body>

	<%-- 인증 헤더 --%>
	<jsp:include page="/WEB-INF/views/common/header.jsp"/>

    <div class="container mt-3">
        <h2>수정</h2>
        <hr>
        <div class="mb-3 mt-3 clearfix">
            <span class="float-start me-2">uid: ${dto.uid }</span>
            <span class="float-end ms-4">작성일: ${dto.regDateTime }</span>
            <span class="float-end">조회수: ${dto.viewCnt }</span>
        </div>

        <form name="frm" action="updateOk.do" method="post"
        	enctype="Multipart/form-data"> <%-- 글 수정 단계에서 파일 추가 (업로딩) 가능 --%>
            <input type="hidden" name="uid" value="${dto.uid }"/>
            <div class="mb-3">
                <label for="name">작성자:</label>
                <span class="form-control">${dto.user.username } (${dto.user.name })</span>
            </div>    
            <div class="mb-3 mt-3">
                <label for="subject">제목:</label>
                <input type="text" class="form-control" id="subject" placeholder="제목을 입력하세요" name="subject" value="${dto.subject }" required>
            </div>
            <div class="mb-3 mt-3">
                <label for="content">내용:</label>
                <textarea class="form-control" rows="5" id="content" placeholder="내용을 입력하세요" name="content">${dto.content }</textarea>
            </div>


            <!-- 기존 첨부파일  목록 (삭제 가능) -->
            <c:if test="${fn:length(fileList) > 0 }">
            <div class="container mt-3 mb-3 border rounded">
                <div id="delFiles"></div>  <!-- 삭제할 file 의 uid 값(들)을 담기위한 div -->
                <div class="mb-3 mt-3">
                    <label>첨부파일:</label>
                    <c:forEach var="fileDto" items="${fileList }">
                    <div class="input-group mb-2">
                        <input class="form-control col-xs-3" type="text" readonly value="${fileDto.source }">
                        <button type="button" class="btn btn-outline-danger" onclick="deleteFiles(${fileDto.uid}); $(this).parent().remove();">삭제</button>
                    </div>
                    </c:forEach>
                </div>
            </div>
            </c:if>
            <script>
            function deleteFiles(fileUid){
            	// 삭제할 file 의 uid 값(들)을 #delFiles 에 담아 submit 한다
				$("#delFiles").append("<input type='hidden' name='delfile' value='" + fileUid + "'>");
            }
            </script>
            <!-- 기존 첨부파일  목록 (삭제 가능) -->

            <!-- 새로운 첨부파일 추가 -->
            <!-- jQuery 사용! -->
            <div class="container mt-3 mb-3 border rounded">
                <div class="mb-3 mt-3">
                    <label>첨부파일추가:</label>
                    <div id="files">
                    
                    </div>
                    <button type="button" id="btnAdd" class="btn btn-secondary">추가</button>
                </div>
            </div>
            
            <script>
			var i = 0;
			$("#btnAdd").click(function(){
				$("#files").append(
						  '<div class="input-group mb-2">'
                        +   '<input class="form-control col-xs-3" type="file" name="upfile' + i + '"/>'
                        +   '<button type="button" class="btn btn-outline-danger" onclick="$(this).parent().remove()">삭제</button>'
                        + '</div>');
				i++;
			});
            </script>
            
            <!-- 새로운 첨부파일 추가 -->


            <button type="submit" class="btn btn-outline-dark">수정완료</button>
            <button type="button" class="btn btn-outline-dark" onclick="history.back()">이전으로</button>
            <a class="btn btn-outline-dark" href="list.do?page=${page != null ? page : ''} ">목록</a>

        </form>
    </div>
</body>

</html>

	</c:otherwise>
</c:choose>

UpdateCommand.java 수정
1. MultipartRequest 생성 -> 파일 업로드 됨  
2. 새로 업로드된 파일의   '원본이름(들)' 과 '저장된 이름(들)' 받아오기
3. 기존첨부파일중 삭제될 첨부파일(들) →  DB에서 삭제, 물리적 파일도 삭제
4. 입력한 값을 받아오기 →  글 수정
5. 추가된 첨부파일(들)  →  DB 에 저장 

 

package command.write;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.oreilly.servlet.MultipartRequest;
import com.oreilly.servlet.multipart.DefaultFileRenamePolicy;
import com.oreilly.servlet.multipart.FileRenamePolicy;

import command.Command;
import domain.FileDAO;
import domain.WriteDAO;
import domain.WriteDTO;

public class UpdateCommand implements Command {

	@Override
	public void execute(HttpServletRequest request, HttpServletResponse response) throws IOException {
		
		// 첨부파일
		FileDAO fileDao;
		
		//----------------------------------------------------------
		// 1. 새로 업로드할 파일(들) MultiPartRequest
		ServletContext context = request.getServletContext();
		// 서블릿 상의 upload 폴더 경로를 알아온다
		String saveDirectory = context.getRealPath("upload");
		System.out.println("업로드 경로: " + saveDirectory);
		
		int maxPostSize = 5 * 1024 * 1024;  // POST 받기, 최대 5M byte :1Kbyte = 1024 byte, 1Mbyte = 1024 Kbyte
		String encoding = "utf-8";  // response 인코딩
		FileRenamePolicy policy = new DefaultFileRenamePolicy(); //업로딩 파일 이름 중복에 대한 정책
		MultipartRequest multi = null; // com.oreilly.servlet.MultipartRequest 임포트

		// MultipartRequest 생성 단계에서 이미 파일은 저장됨.
		multi = new MultipartRequest(
				request,                 // JSP 내부객체 request
				saveDirectory,
				maxPostSize,
				encoding,
				policy
				);

		// 2. 새로 업로드된 파일의   '원본이름(들)' 과 '저장된 이름(들)' 받아오기 
		List<String> originalFileNames = new ArrayList<String>();
		List<String> fileSystemNames = new ArrayList<String>();
		
		Enumeration names = multi.getFileNames();   // type="file" 요소 name들 추출		
		while(names.hasMoreElements()){		
			String name = (String)names.nextElement();  
			String originalFileName = multi.getOriginalFileName(name);
			String fileSystemName = multi.getFilesystemName(name);
			System.out.println("첨부파일: " + originalFileName + "->" + fileSystemName);
			
			if(originalFileName != null && fileSystemName != null) {
				originalFileNames.add(originalFileName);
				fileSystemNames.add(fileSystemName);
			}
		} // end while		
		
		//----------------------------------------------------------
		// 3. 삭제될 기존의 첨부파일(들) -> DB에서 삭제, 물리적 파일도 삭제		
		String [] delFiles = multi.getParameterValues("delfile");
		if(delFiles != null && delFiles.length > 0) {  // 삭제될 첨부파일이 있다면
			// String[] -> int[] 변환
			int [] delFileUids = new int[delFiles.length];
			for (int i = 0; i < delFileUids.length; i++) {
				delFileUids[i] = Integer.parseInt(delFiles[i]);
			}
			
			try {				
				new FileDAO().deleteByUid(delFileUids, request);
			} catch(SQLException e) {
				e.printStackTrace();
			}
		}
		
		
		//입력한 값을 받아오기
		int uid = Integer.parseInt(multi.getParameter("uid"));
		String subject = multi.getParameter("subject");
		String content = multi.getParameter("content");
		
		int cnt = 0;
		WriteDAO dao = new WriteDAO();
		
		WriteDTO dto = new WriteDTO();
		dto.setUid(uid);
		dto.setSubject(subject);
		dto.setContent(content);
		
		try {
			cnt = dao.update(dto);
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
		// 5. 추가된 첨부파일(들) -> DB 에 저장
		try {
			new FileDAO().insert(uid, originalFileNames, fileSystemNames);
		} catch (SQLException e) {
			e.printStackTrace();
		}

		request.setAttribute("result", cnt);
		request.setAttribute("dto", dto);
		

	}

}

* UpdateOk.jsp 수정
업데이트 수정후 view.do 로 이동하려면 multipart, request 로 처리했으니
updateOk.jsp 에 있던 ${param.uid} 가 작동 안 할 것이다.
- UpdateCommand.java 수정!
- updateOk.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:choose>
	<c:when test="${result == 0 }">
	<script>
		alert("수정 실패");
		history.back();
	</script>
	</c:when>
	<c:otherwise>
	<script>
		alert("수정 성공");
		location.href = "view.do?uid=${dto.uid }";
	</script>
	</c:otherwise>
</c:choose>

'JSP' 카테고리의 다른 글

[JSP] Comment  (0) 2022.04.13
[JSP] FileUpload / Download  (0) 2022.04.11
[JSP] Pagination  (0) 2022.04.11
[JSP] Authentification - 회원제 게시판  (0) 2022.04.10
[JSP] Authentication - 로그인/로그아웃  (0) 2022.04.10