이번에는 파일 다운로드 녀석이다.
우선 파일을 다운로드 하려면 저장된 파일 이름을 가져와야한다.(당연한 소리다.)
이전 업로드 글에서 얘기했듯이 db에는 originalFileName과 saveFileName이 들어있다.
<a href="#" onclick = "javascript:file_download('saveFileName'); return false;">originalFileName</a>
여기서 originalFileName으로 사용자에게 파일 이름을 보여주고 다운로드 기능에는 saveFileName을 보내주는 것이다.
그럼 스크립트에서
function file_download(saveFileName ) {
var form = document.getElementById("thisForm");
form.cmd.value = "FILE_DOWNLOAD";
form.file1.value = saveFileName;
form.submit();
}
컨트롤러로 saveFileName을 보낸다.
(본인은 어쩔 수 없이 javascript에서 form으로 보냈지만 그럴 필요 없다. a태그에서 바로 호출 해도 무방하다.)
public void fileDownload(WebForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String forward = "NOTICE_VIEW";
String fileName = form.getFile1(); // 전달 받은 saveFileName
String orgFileName = form.getFile2(); // 이건 안받아도 무방하지만 originalFileName
BufferedInputStream in = null;
BufferedOutputStream out = null;
try {
String realPath = "";
realPath = PropertyUtil.getValue("notice.file.path")+fileName; // application.properties 파일에 선언된 파일 경로 + saveFileName
File file = new File(realPath);
String pathChcek = PropertyUtil.getValue("notice.file.path"); // application.properties 파일에 선언된 파일 경로
if(!file.getCanonicalPath().replace(File.separator, "/").startsWith(pathChcek)) { // 지정된 파일 경로가 아닌 다른 파일 경로를 요청할 경우(보안) *중요
System.err.println("잘못된 접근입니다.");
throw new ServletException("잘못된 접근입니다.");
}
FileInputStream fis = new FileInputStream(file);
response.setContentLength((int) file.length()); // 파일 길이 확인
in = new BufferedInputStream(fis);
out = new BufferedOutputStream(response.getOutputStream());
String encodedFilename = "attachment; filename*=" + "UTF-8" + "''" + URLEncoder.encode(orgFileName, "UTF-8"); // 파일명을 URLEcoder 하여 attachment, Content-Disposition Header로 설정
response.setContentType("application/octet-stream; charset=utf-8");
response.setHeader("Content-Disposition", encodedFilename);
int readBytes = 0;
byte[] buffer = new byte[4096];
while ((readBytes = in.read()) != -1) {
out.write(buffer, 0, readBytes);
}
out.flush(); // 버퍼에 남은 내용이 있으면 모두 출력
} catch (IOException ioe) {
throw new ServletException(ioe.getMessage());
} finally {
// in, out 스트림 닫기 (메모리 누수 방지)
if (in != null) in.close();
if (out != null) out.close();
}
}
위 코드와 같이
BufferedOutputStream
를 사용하여 파일을 다운로드 받을 수 있다.
여기서 본인이 가장 중요하게 생각하는 것은 코드에도 '*중요' 라고 표시한 부분이다.
프론트엔드에서 fileDownload를 호출할 때 다운로드 url을 임의로 바꿔서 서버에 있는 엉뚱한 파일을 다운로드 받을 수 있다.
그래서 그것을 방지하고자 요청 받은 파일 위치가 선언 되어있는 파일 위치와 다를경우에는 파일 다운로드를 하지 않고 예외처리를 할 수 있는 것이다.
항상 보안에 신경 쓰면서 개발을 해야겠다.
아래는 서블릿으로 처리한 파일 다운로드 코드이다.
(BufferedOutputStream이 아닌 PrintWriter를 사용한 방식이다.
이런 것도 있구나 하고 코드를 그냥 한 번 보기만 해도 괜찮겠다.)
public void fileDownload(WebForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String forward = "NOTICE_VIEW";
String fileName = form.getFile1(); // 전달 받은 saveFileName
String orgFileName = form.getFile2(); // 이건 안받아도 무방하지만 originalFileName
PrintWriter stream = null;
BufferedInputStream buf = null;
try {
String realPath = "";
realPath = PropertyUtil.getValue("notice.file.path")+fileName;
File file = new File(realPath);
String pathChcek = PropertyUtil.getValue("notice.file.path");
if(!file.getCanonicalPath().replace(File.separator, "/").startsWith(pathChcek)) { // 지정된 파일 경로가 아닌 다른 파일 경로를 요청할 경우(보안) *중요
System.err.println("잘못된 접근입니다.");
throw new ServletException("잘못된 접근입니다.");
}
stream = response.getWriter();
FileInputStream fis = new FileInputStream(file);
response.setContentLength((int) file.length()); // 파일 길이 확인
buf = new BufferedInputStream(fis);
String encodedFilename = "attachment; filename*=" + "UTF-8" + "''" + URLEncoder.encode(orgFileName, "UTF-8"); // 파일명을 URLEcoder 하여 attachment, Content-Disposition Header로 설정
response.setContentType("application/octet-stream; charset=utf-8");
response.setHeader("Content-Disposition", encodedFilename);
int readBytes = 0;
byte[] buffer = new byte[4096];
while ((readBytes = buf.read()) != -1) {
stream.write(readBytes);
}
} catch (IOException ioe) {
throw new ServletException(ioe.getMessage());
} finally {
if (stream != null) stream.close();
if (buf != null) buf.close();
}
}
이로써 웹프로젝트에서 정말 많이 사용되는 파일 업로드 / 다운로드를 알아보았다.
거의 안쓰이는 곳이 없을정도로 많이 쓰이니 본인이 '파일 업로드 / 다운로드 정도는 뭐 쉽지' 라고 생각해도 한 번쯤은 싹 정리를 해보는 것도 좋을 거 같다.