글 삭제 시 첨부파일 삭제
‘글 수정’ 에 앞서 ‘글 삭제’ 부터 만들어 두면 좋다.
글 삭제시 두가지를 삭제 한다
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 |