프로그래밍/스프링 & 스프링 부트
스프링 서블릿 api를 이용한 파일 업로드, 다운로드(3)-수정, 삭제
밍구몬
2019. 6. 25. 17:24
수정하기는 기존의 어떤 파일을 수정했는지 찾아서 수정하지 않고 첨부파일 리스트를 지우고 다시 등록하는 방식으로 할 것이다.
modify.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>
<%@taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<%@taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>글 수정</title>
<script src="resources/jQuery/jquery-3.4.1.min.js"></script>
</head>
<body>
<h1>글 수정</h1>
<hr>
<form id="frm" action="updateBoard.do" method="post" enctype="multipart/form-data">
<input name="seq" type="hidden" value="${board.idx}" />
<table border="1">
<tr>
<td bgcolor="orange" width="70">제목</td>
<td align="left"><input id="title" name="title" type="text"
value="${board.title }" /></td>
</tr>
<tr>
<td bgcolor="orange">작성자</td>
<td align="left">${board.writer }</td>
</tr>
<tr>
<td bgcolor="orange">내용</td>
<td align="left"><textarea id="content" name="content" cols="40" rows="10">${board.content }</textarea></td>
</tr>
<tr>
<td bgcolor="orange">등록일</td>
<td align="left"><fmt:formatDate value="${board.regDate }" pattern="yyyy-MM-dd"/></td>
</tr>
<tr>
<td colspan="2" align="center">
<input type="file" name="uploadFile" id="uploadFile" multiple>
<div id="preview"></div>
</td>
</tr>
<tr>
<td bgcolor="orange">조회수</td>
<td align="left">${board.cnt }</td>
</tr>
<tr>
<td colspan="2" align="center"><input type="button" id="modify" value="글 수정" /></td>
</tr>
</table>
</form>
<hr>
<a href="writeBoard.do">글 쓰기</a>
<a href="deleteBoard.do?idx=${board.idx }">글 삭제</a>
<a id="list" href="getBoardList.do">글 목록</a>
</body>
<script type="text/javascript">
$(document).ready(function (e){
//수정하기 버튼
$('#modify').click(function(){
var frmArr = ["title","content"];
$('#frm').append("<input type='hidden' name='pageNum' value='<c:out value='${cri.pageNum }'/>'>");
$('#frm').append("<input type='hidden' name='amount' value='<c:out value='${cri.amount }'/>'>");
//입력 값 널 체크
for(var i=0;i<frmArr.length;i++){
//alert(arr[i]);
if($.trim($('#'+frmArr[i]).val()) == ''){
alert('빈 칸을 모두 입력해 주세요. -'+frmArr[i]);
$('#'+frmArr[i]).focus();
return false;
}
}
//전송
$('#frm').submit();
});
//글 목록
$('#list').click(function(e){
e.preventDefault();
var $form = $('<form></form>');
$form.attr('action','getBoardList.do');
$form.attr('method','get');
$form.appendTo('body');
$form.append("<input type='hidden' name='pageNum' value='<c:out value='${cri.pageNum}'/>'>");
$form.append("<input type='hidden' name='amount' value='<c:out value='${cri.amount}'/>'>");
$form.submit();
});
//첨부파일
(function(){
var bno='<c:out value="${board.idx}"/>';
console.log(bno);
console.log('------');
$.getJSON("/getAttachList", {bno: bno}, function(arr){
console.log(arr);
$(arr).each(function(i, attach){
var imgPath = '/img/'+attach.path+'/'+attach.uuid;
//div에 첨부파일 이미지 추가
var str = '<div style="display: inline-flex; padding: 10px;"><li>';
str += '<span>'+attach.fileName+'</span><br>';
str += '<img src="'+imgPath+'" title="'+attach.fileName+'" onerror="this.src=\'/resources/img/fileImg.png\'" width=100 height=100 />';
str += '</li></div>';
$(str).appendTo('#preview');
});//arr each
}); //end getJSON
})(); //end function
$("input[type='file']").change(function(e){
//div 내용 비워주기
$('#preview').empty();
var files = e.target.files;
var arr =Array.prototype.slice.call(files);
//업로드 가능 파일인지 체크
for(var i=0;i<files.length;i++){
if(!checkExtension(files[i].name,files[i].size)){
return false;
}
}
preview(arr);
});//file change
function checkExtension(fileName,fileSize){
var regex = new RegExp("(.*?)\.(exe|sh|zip|alz)$");
var maxSize = 20971520; //20MB
if(fileSize >= maxSize){
alert('파일 사이즈 초과');
$("input[type='file']").val(""); //파일 초기화
return false;
}
if(regex.test(fileName)){
alert('업로드 불가능한 파일이 있습니다.');
$("input[type='file']").val(""); //파일 초기화
return false;
}
return true;
}
function preview(arr){
arr.forEach(function(f){
//파일명이 길면 파일명...으로 처리
var fileName = f.name;
if(fileName.length > 10){
fileName = fileName.substring(0,7)+"...";
}
//div에 이미지 추가
var str = '<div style="display: inline-flex; padding: 10px;"><li>';
str += '<span>'+fileName+'</span><br>';
//이미지 파일 미리보기
if(f.type.match('image.*')){
var reader = new FileReader(); //파일을 읽기 위한 FileReader객체 생성
reader.onload = function (e) { //파일 읽어들이기를 성공했을때 호출되는 이벤트 핸들러
//str += '<button type="button" class="delBtn" value="'+f.name+'" style="background: red">x</button><br>';
str += '<img src="'+e.target.result+'" title="'+f.name+'" width=100 height=100 />';
str += '</li></div>';
$(str).appendTo('#preview');
}
reader.readAsDataURL(f);
}else{
str += '<img src="/resources/img/fileImg.png" title="'+f.name+'" width=100 height=100 />';
$(str).appendTo('#preview');
}
});//arr.forEach
}
});
</script>
</html>
modify.jsp는 이전 content.jsp에 약간만 수정해서 내용은 비슷하다.
content.jsp처럼 페이지가 로딩될 때 ajax로 첨부파일 미리보기를 만들어 주며, 파일을 업로드 할 수 있게 만들었다.
form은 enctype="multipart/form-data"을 추가하여 파일을 전송할 수 있도록 만들었으며, multiple 속성으로 여러개의 파일을 추가할 수 있다.
BoardController
// 글 수정
@RequestMapping("/updateBoard.do")
public String updateBoard(@ModelAttribute("board") BoardVO vo,MultipartFile[] uploadFile , @ModelAttribute("cri") Criteria cri,RedirectAttributes rttr) {
if(boardService.updateBoard(vo,uploadFile)) {
rttr.addFlashAttribute("result","success");
}
rttr.addAttribute("amount",cri.getAmount());
rttr.addAttribute("pageNum",cri.getPageNum());
return "redirect:getBoardList.do";
}
업로드 파일을 받아올 수 있도록 MultipartFile[] uploadFile을 추가해 주었다. upload파일을 받은 뒤 service로 vo와 uploadFile을 보낸다.
BoardServiceImpl
@Override
@Transactional
public boolean updateBoard(BoardVO vo, MultipartFile[] uploadFile) {
List<FileVO> list = boardDAO.findByBno(vo.getIdx());
//첨부된 파일이 없으면 게시글만 수정
if(uploadFile[0].getOriginalFilename().equals("")) {
return boardDAO.updateBoard(vo);
}
//기존에 첨부 파일이 있으면
if(boardDAO.selectAttach(vo.getIdx())) {
//db지우기
boardDAO.delAttach(vo.getIdx());
//파일 지우기
deleteFiles(list);
}
//파일 저장및 list 세팅
List<FileVO> saveList = setFileList(uploadFile, vo.getIdx());
//파일 디비에 추가
boardDAO.insertAttach(saveList);
return boardDAO.updateBoard(vo);
}
private void deleteFiles(List<FileVO> list) {
list.forEach(attach -> {
try {
Path file = Paths.get("D:\\upload\\"+attach.getPath()+"\\"+attach.getUuid());
System.out.println(file);
Files.deleteIfExists(file);
}catch(Exception e) {
System.out.println(e);
}
});//end forEach
}
첨부파일이 변경되지 않았다면 게시글만 수정하고, 첨부파일이 변경되었다면 해당 db와 파일을 지우고 다시 첨부파일을 등록시켜주었다.
일관성을 위하여 @Transcational 어노테이션을 사용하였다.
BoardDAO
public boolean selectAttach(long bno) {
return mybatis.selectOne("BoardMapper.selectAttach",bno);
}
public void delAttach(long bno) {
mybatis.delete("BoardMapper.delAttach",bno);
}
BoardMapper(testMapper)
<select id="selectAttach" resultType="boolean" parameterType="long">
<![CDATA[
SELECT count(*)
FROM attachments
WHERE bno=#{bno} and rownum < 2
]]>
</select>
<delete id="delAttach" parameterType="long">
<![CDATA[
DELETE FROM attachments
WHERE bno=#{bno}
]]>
</delete>
여기까지가 게시글 수정이다.
게시글 삭제는 BoardServiceImpl만 조금 수정해 주면 된다.
@Override
@Transactional
public void deleteBoard(BoardVO vo) {
List<FileVO> list = boardDAO.findByBno(vo.getIdx());
if(list.size() > 0) {
boardDAO.delAttach(vo.getIdx()); //db지우기
deleteFiles(list); //파일 지우기
}
boardDAO.deleteBoard(vo); //게시글 삭제
}