파일 업로드 / 다운로드 (다운로드)

TheCarDeveloper·2023년 8월 23일
0

Web

목록 보기
6/6

이번에는 파일 다운로드 녀석이다.

우선 파일을 다운로드 하려면 저장된 파일 이름을 가져와야한다.(당연한 소리다.)

이전 업로드 글에서 얘기했듯이 db에는 originalFileNamesaveFileName이 들어있다.

<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();
   }

}
 

이로써 웹프로젝트에서 정말 많이 사용되는 파일 업로드 / 다운로드를 알아보았다.

거의 안쓰이는 곳이 없을정도로 많이 쓰이니 본인이 '파일 업로드 / 다운로드 정도는 뭐 쉽지' 라고 생각해도 한 번쯤은 싹 정리를 해보는 것도 좋을 거 같다.

0개의 댓글