스프링부트, 타임리프(Thymeleaf)로 파일게시판 만들기
#9 파일 업로드, 다운로드 게시판
다운로드 구현
지난글에서 업로드를 구현할 때 file 테이블에 외래키를 두어 file_board 테이블과 연동하도록 했습니다. 즉, 파일을 업로드 할 때 해당 게시판 번호인 'b_no'를 외래키로 받아 file 테이블에 FileVO 객체를 통해 저장하도록 했습니다. 게시글 상세내용에서 업로드한 파일을 확인하려면, 먼저 mapper를 통해 Database에서 해당 파일을 게시판 번호를 기반으로 조회해서 가져오면 됩니다. 그리고 컨트롤러와 detail.html에서 다운로드를 위한 작업을 해주면 됩니다.
FileBoardMapper.java
package com.example.demo.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.example.demo.bean.FileBoardVO;
import com.example.demo.bean.FileVO;
@Mapper
public interface FileBoardMapper {
...
//파일 업로드 및 다운로드 메서드 추가
int fileInsert(FileVO file);
FileVO fileDetail(int b_no);
}
FileBoardService.java
package com.example.demo.service;
import java.util.List;
import com.example.demo.bean.FileBoardVO;
import com.example.demo.bean.FileVO;
public interface FileBoardService {
...
//파일 업로드 및 다운로드 메서드 추가
int fileInsert(FileVO file);
FileVO fileDetail(int b_no);
}
FileBoardServiceImpl.java
package com.example.demo.service;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.demo.bean.FileBoardVO;
import com.example.demo.bean.FileVO;
import com.example.demo.mapper.FileBoardMapper;
@Service
public class FileBoardServiceImpl implements FileBoardService {
@Autowired
FileBoardMapper fileboardmapper;
...
//파일 업로드 및 다운로드 추가
@Override
public int fileInsert(FileVO file) {
return fileboardmapper.fileInsert(file);
}
@Override
public FileVO fileDetail(int b_no) {
return fileboardmapper.fileDetail(b_no);
}
}
FileBoardMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTDMapper3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.FileBoardMapper">
...
<!-- 파일 업로드 및 다운로드 관련 추가 -->
<insert id="fileInsert" parameterType="com.example.demo.bean.FileVO">
<selectKey keyProperty="b_no" resultType="int" order="BEFORE">
SELECT MAX(b_no)
FROM file_board
</selectKey>
INSERT INTO file(b_no, filename, fileoriginname, fileurl)
VALUES(#{b_no}, #{filename}, #{fileoriginname}, #{fileurl})
</insert>
<select id="fileDetail" parameterType="int" resultType="com.example.demo.bean.FileVO">
SELECT * FROM file WHERE b_no=#{b_no}
</select>
</mapper>
이제 컨트롤러에서 작업하겠습니다. 먼저 게시글 상세내용을 보기 위해 게시글을 클릭했을 때 업로드한 파일명을 볼 수 있도록 Database에서 가져와야 합니다. @RequestMapping("/detail/{b_no}")에 다음 코드를 추가로 작성해주세요.
FileBoardController.java
...
@RequestMapping("/detail/{b_no}")
private String fileBoardDetail(@PathVariable("b_no") int b_no, Model model) {
model.addAttribute("detail", fboardService.fileBoardDetail(b_no));
if(fboardService.fileDetail(b_no) == null) {
return "fileBoard/detail";
} else {
model.addAttribute("file", fboardService.fileDetail(b_no));
return "fileBoard/detail";
}
}
...
이제 detail.html에서 업로드한 파일명을 확인할 수 있도록 작업하겠습니다.
detail.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>파일게시판 - 상세페이지</title>
</head>
<body>
<div>
...
<div>
<label style="font-weight: bolder;">첨부파일</label>
<p>
<a th:if="${file}" th:href="@{/fileBoard/fileDown/{bno}(bno=${file.b_no})}"
>[[${file.fileoriginname}]]</a>
</p>
<!--/* 잘 찍히나 확인 => <br>[[${file.b_no}]] */-->
</div>
...
</div>
</body>
</html>
작동이 잘 되는지 localhost:8080/fileBoard/list에 들어가서 확인해보세요.
이전 게시글에서 올린 첨부파일이 출력되었습니다. 하지만 버튼을 누르면 다운로드가 되지 않고 에러가 뜹니다. 이 작업만 해주면 다운로드는 끝납니다. list.html에서 첨부파일 항목을 넣을 때 <a> 태그에서 th:href 속성을 작성했습니다.
<div>
<label style="font-weight: bolder;">첨부파일</label>
<p>
<a th:if="${file}" th:href="@{/fileBoard/fileDown/{bno}(bno=${file.b_no})}">
[[${file.fileoriginname}]]
</a>
</p>
</div>
따라서 ,이 <a> 태그를 눌렀을 때 컨트롤러에서 저 url로 요청을 받아 다운로드 처리를 해주면 됩니다. 컨트롤러에서 저 요청을 처리할 코드를 아래처럼 작성해주시면 됩니다.
FileBoardController.java
@RequestMapping("/fileDown/{b_no}")
private void fileDown(@PathVariable("b_no") int b_no, HttpServletRequest request,
HttpServletResponse response) throws UnsupportedEncodingException, Exception {
request.setCharacterEncoding("UTF-8");
FileVO fileVO = fboardService.fileDetail(b_no);
//파일 업로드 경로
try {
String fileUrl = fileVO.getFileurl();
System.out.println(fileUrl);
fileUrl += "/";
String savePath = fileUrl;
String fileName = fileVO.getFilename();
//실제 내보낼 파일명
String originFileName = fileVO.getFileoriginname();
InputStream in = null;
OutputStream os = null;
File file= null;
Boolean skip = false;
String client = "";
//파일을 읽어 스트림에 담기
try {
file = new File(savePath, fileName);
in = new FileInputStream(file);
} catch (FileNotFoundException fe) {
skip = true;
}
client = request.getHeader("User-Agent");
//파일 다운로드 헤더 지정
response.reset();
response.setContentType("application/octet-stream");
response.setHeader("Content-Description", "HTML Generated Data");
if(!skip) {
//IE
if(client.indexOf("MSIE") != -1) {
response.setHeader("Content-Disposition", "attachment; filename=\""
+ java.net.URLEncoder.encode(originFileName, "UTF-8").replaceAll("\\+", "\\ ") + "\"");
//IE 11 이상
} else if (client.indexOf("Trident") != -1) {
response.setHeader("Content-Disposition", "attachment; filename=\""
+ java.net.URLEncoder.encode(originFileName, "UTF-8").replaceAll("\\+", "\\ ") + "\"");
//한글 파일명 처리
} else {
response.setHeader("Content-Disposition", "attachment; filename=\"" +
new String(originFileName.getBytes("UTF-8"), "ISO8859_1") + "\"");
response.setHeader("Content-Type", "application/octet-stream; charset=utf-8");
}
response.setHeader("Content-Length", ""+file.length());
os = response.getOutputStream();
byte b[] = new byte[(int) file.length()];
int leng = 0;
while ((leng = in.read(b)) > 0) {
os.write(b, 0, leng);
}
} else {
response.setContentType("text/html; charset=UTF-8");
PrintWriter out = response.getWriter();
out.println("<script> alert('파일을 찾을 수 없습니다.'); history.back(); </script>");
out.flush();
}
in.close();
os.close();
} catch (Exception e) {
System.out.println("ERROR : " + e.getStackTrace());
}
}
이제 서버를 열고 localhlst:8080/fileBoard/list에 들어가서 파일 다운로드가 잘 되는지 확인해보세요.
댓글