26. 스프링 게시판 만들기 / 첨부파일 업로드, 다운로드(3)
안녕하세요.
이전에는 첨부파일 조회를 했었는데요.
이번에는 첨부파일 다운로드를 만들어보겠습니다..
1. boardMapper.xml 작성
첨부파일의 번호를 조회하는 쿼리를 작성합니다.
<!-- 첨부파일 다운 -->
<select id="selectFileInfo" parameterType="hashMap" resultType="hashMap">
SELECT
STORED_FILE_NAME,
ORG_FILE_NAME
FROM MP_FILE
WHERE FILE_NO = #{FILE_NO}
</select>
2. BoardDAO 작성
boardMapper.xml에 파라미터를 잘 보내줄 수 있도록 코드를 작성해줍니다.
BoardDAO
// 첨부파일 다운
public Map<String, Object> selectFileInfo(Map<String, Object> map) throws Exception;
BoardDAOImpl
// 첨부파일 다운로드
@Override
public Map<String, Object> selectFileInfo(Map<String, Object> map) throws Exception {
// TODO Auto-generated method stub
return sqlSession.selectOne("boardMapper.selectFileInfo", map);
}
3. BoardService 작성
파라미터를 BoardDAO로 전달할 수 있도록 코드를 작성해 줍니다.
BoardService
// 첨부파일 다운'
public Map<String, Object> selectFileInfo(Map<String, Object> map) throws Exception;
BoardServiceImpl
// 첨부파일 다운로드
@Override
public Map<String, Object> selectFileInfo(Map<String, Object> map) throws Exception {
// TODO Auto-generated method stub
return dao.selectFileInfo(map);
}
4. BoardController 작성
HttpServletResponse response는 처음에 첨부파일을 업로드 할때는 MultipartHttpServletRequest mpRequest을 이용하여 서버에 요청을 했었는데요. request는 jsp화면에서 서버로 요청할 때 쓰고, response는 서버에서 jsp화면으로 응답할때에 쓰입니다. 그래서 파일정보들을 responses에 담아 처리를 합니다.
다운로드를 만드시고 난 후에 개발자모드 (F12)를 누르고 Network로 가신 후 파일을 다운로드 클릭하면 fileDown이 나옵니다. Headers쪽에 빨간 박스를 보시면 저희가 response에 set한 값들이 보여지는게 보입니다. 이런식으로 속성들에 어떤 값들이 들어갔는지 확인할 수 있습니다 flush() 메소드는 데이터를 비워주는? 역할을 하고 close() 닫아주는 역할을 합니다.
@RequestMapping(value="/fileDown")
public void fileDown(@RequestParam Map<String, Object> map, HttpServletResponse response) throws Exception{
Map<String, Object> resultMap = service.selectFileInfo(map);
String storedFileName = (String) resultMap.get("STORED_FILE_NAME");
String originalFileName = (String) resultMap.get("ORG_FILE_NAME");
// 파일을 저장했던 위치에서 첨부파일을 읽어 byte[]형식으로 변환한다.
byte fileByte[] = org.apache.commons.io.FileUtils.readFileToByteArray(new File("C:\\mp\\file\\"+storedFileName));
response.setContentType("application/octet-stream");
response.setContentLength(fileByte.length);
response.setHeader("Content-Disposition", "attachment; fileName=\""+URLEncoder.encode(originalFileName, "UTF-8")+"\";");
response.getOutputStream().write(fileByte);
response.getOutputStream().flush();
response.getOutputStream().close();
}
5. readView 작성
이전 포스팅에서 a태그에 onclick에 함수를 넣어놨는데요.
파라미터로 파일의 번호를 넘겨주고 그다음 id가 FILE_NO인 input태그의 값에 파일의 번호를 넘겨준 후
컨트롤러로 값을 보내줍니다. (/board/fileDown)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<html>
<head>
<!-- 합쳐지고 최소화된 최신 CSS -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<!-- 부가적인 테마 -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<title>게시판</title>
</head>
<script type="text/javascript">
$(document).ready(function(){
var formObj = $("form[name='readForm']");
// 수정
$(".update_btn").on("click", function(){
formObj.attr("action", "/board/updateView");
formObj.attr("method", "get");
formObj.submit();
})
// 삭제
$(".delete_btn").on("click", function(){
var deleteYN = confirm("삭제하시겠습니까?");
if(deleteYN == true){
formObj.attr("action", "/board/delete");
formObj.attr("method", "post");
formObj.submit();
}
})
// 목록
$(".list_btn").on("click", function(){
location.href = "/board/list?page=${scri.page}"
+"&perPageNum=${scri.perPageNum}"
+"&searchType=${scri.searchType}&keyword=${scri.keyword}";
})
$(".replyWriteBtn").on("click", function(){
var formObj = $("form[name='replyForm']");
formObj.attr("action", "/board/replyWrite");
formObj.submit();
});
//댓글 수정 View
$(".replyUpdateBtn").on("click", function(){
location.href = "/board/replyUpdateView?bno=${read.bno}"
+ "&page=${scri.page}"
+ "&perPageNum=${scri.perPageNum}"
+ "&searchType=${scri.searchType}"
+ "&keyword=${scri.keyword}"
+ "&rno="+$(this).attr("data-rno");
});
//댓글 삭제 View
$(".replyDeleteBtn").on("click", function(){
location.href = "/board/replyDeleteView?bno=${read.bno}"
+ "&page=${scri.page}"
+ "&perPageNum=${scri.perPageNum}"
+ "&searchType=${scri.searchType}"
+ "&keyword=${scri.keyword}"
+ "&rno="+$(this).attr("data-rno");
});
})
function fn_fileDown(fileNo){
var formObj = $("form[name='readForm']");
$("#FILE_NO").attr("value", fileNo);
formObj.attr("action", "/board/fileDown");
formObj.submit();
}
</script>
<body>
<div class="container">
<header>
<h1> 게시판</h1>
</header>
<hr />
<div>
<%@include file="nav.jsp" %>
</div>
<section id="container">
<form name="readForm" role="form" method="post">
<input type="hidden" id="bno" name="bno" value="${read.bno}" />
<input type="hidden" id="page" name="page" value="${scri.page}">
<input type="hidden" id="perPageNum" name="perPageNum" value="${scri.perPageNum}">
<input type="hidden" id="searchType" name="searchType" value="${scri.searchType}">
<input type="hidden" id="keyword" name="keyword" value="${scri.keyword}">
<input type="hidden" id="FILE_NO" name="FILE_NO" value="">
</form>
<div class="form-group">
<label for="title" class="col-sm-2 control-label">제목</label>
<input type="text" id="title" name="title" class="form-control" value="${read.title}" readonly="readonly" />
</div>
<div class="form-group">
<label for="content" class="col-sm-2 control-label">내용</label>
<textarea id="content" name="content" class="form-control" readonly="readonly"><c:out value="${read.content}" /></textarea>
</div>
<div class="form-group">
<label for="writer" class="col-sm-2 control-label">작성자</label>
<input type="text" id="writer" name="writer" class="form-control" value="${read.writer}" readonly="readonly"/>
</div>
<div class="form-group">
<label for="regdate" class="col-sm-2 control-label">작성날짜</label>
<fmt:formatDate value="${read.regdate}" pattern="yyyy-MM-dd" />
</div>
<hr>
<span>파일 목록</span>
<div class="form-group" style="border: 1px solid #dbdbdb;">
<c:forEach var="file" items="${file}">
<a href="#" onclick="fn_fileDown('${file.FILE_NO}'); return false;">${file.ORG_FILE_NAME}</a>(${file.FILE_SIZE}kb)<br>
</c:forEach>
</div>
<hr>
<div>
<button type="button" class="update_btn btn btn-warning">수정</button>
<button type="button" class="delete_btn btn btn-danger">삭제</button>
<button type="button" class="list_btn btn btn-primary">목록</button>
</div>
<!-- 댓글 -->
<div id="reply">
<ol class="replyList">
<c:forEach items="${replyList}" var="replyList">
<li>
<p>
작성자 : ${replyList.writer}<br />
작성 날짜 : <fmt:formatDate value="${replyList.regdate}" pattern="yyyy-MM-dd" />
</p>
<p>${replyList.content}</p>
<div>
<button type="button" class="replyUpdateBtn btn btn-warning" data-rno="${replyList.rno}">수정</button>
<button type="button" class="replyDeleteBtn btn btn-danger" data-rno="${replyList.rno}">삭제</button>
</div>
</li>
</c:forEach>
</ol>
</div>
<form name="replyForm" method="post" class="form-horizontal">
<input type="hidden" id="bno" name="bno" value="${read.bno}" />
<input type="hidden" id="page" name="page" value="${scri.page}">
<input type="hidden" id="perPageNum" name="perPageNum" value="${scri.perPageNum}">
<input type="hidden" id="searchType" name="searchType" value="${scri.searchType}">
<input type="hidden" id="keyword" name="keyword" value="${scri.keyword}">
<div class="form-group">
<label for="writer" class="col-sm-2 control-label">댓글 작성자</label>
<div class="col-sm-10">
<input type="text" id="writer" name="writer" class="form-control" />
</div>
</div>
<div class="form-group">
<label for="content" class="col-sm-2 control-label">댓글 내용</label>
<div class="col-sm-10">
<input type="text" id="content" name="content" class="form-control"/>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="button" class="replyWriteBtn btn btn-success">작성</button>
</div>
</div>
</form>
</section>
<hr />
</div>
</body>
</html>
6. 실행 테스트
다운로드가 잘 된것을 확인합니다.