본문 바로가기
프로그래밍/스프링 & 스프링 부트

스프링 서블릿 api를 이용한 파일 업로드, 다운로드(3)-수정, 삭제

by 밍구몬 2019. 6. 25.

수정하기는 기존의 어떤 파일을 수정했는지 찾아서 수정하지 않고 첨부파일 리스트를 지우고 다시 등록하는 방식으로 할 것이다.

 

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>&nbsp;&nbsp;&nbsp; 
	<a href="deleteBoard.do?idx=${board.idx }">글 삭제</a>&nbsp;&nbsp;&nbsp;
	<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); //게시글 삭제
	}