[ITWILL : JSP]Javabean 14 : 게시판만들기(ServletContext사용하여 파일업로드와 다운로드)

ITWILL학원 : 32강 JSP기초 BY 정규태강사

UTF-8인코딩을 기본으로 ServletContext이용하여 가상경로에 파일을 저장하는 파일업로드와 다운로드기능 구현

1. webcontent 하위 새로운 폴더 2개 생성 : fileuputf, upload

앞으로 파일다운로드기능구현시 생성되는 jsp, html페이지를 이 폴더(fileuputf)안에 생성한다.
업로드되는 파일들은 upload라는 폴더안에 들어가게 된다.=> 가상경로라고 부른다.

2. fileUploadForm.jsp 생성

1
2
3
4
5
6
7
8
<fieldset>
<form action="fileUploadPro.jsp" method="post" enctype="multipart/form-data">
작성자 : <input type="text" name="name"><br>
제목 : <input type="text" name="subject"><br>
파일 : <input type="file" name="file"><br>
<input type="submit" value="파일업로드">
</form>
</fieldset>

3. fileUploadPro.jsp 생성

  1. 가상경로

  2. MultipartRequest객체생성 : request정보를 직접 가져올 수 없고 multi를 통해서 가져올 수 있다.

  3. Enumeration : 다음 요소가 있으면 반환해주는 인터페이스.

  4. 파일업로드 기능은 예외가 많이때문에 예외처리를 해야한다

  5. 사용자가 입력한 데이터를 fileCheck.jsp로 전달하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<!-- 파일 업로드 처리  -->
<%
//1. 파일 저장 위치 (가상경로) 만들기
// D:\workspace_jsp7\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\fileUpload/upload
//System.out.println("가상경로 : "+request.getRealPath("/upload"));
String uploadPath = request.getRealPath("/upload");

//2. 저장 크기 지정 (10MB)
int maxSize = 10 * 1024 * 1024;

//3. 정보를 저장하는 변수 만들기
String name ="";
String subject ="";
String filename = ""; //서버에 올라갈 파일명
String OFilename = ""; //오리지널 파일명

//6. 예외처리
try{
//4. MultipartRequest 객체생성
//4-1. cos 라이브러리 추가
//4-2. 객체 생성
//request정보를 직접가져올 수 없고 multi를 통해서 가져올 수 있다.
MultipartRequest multi = new MultipartRequest(request, uploadPath,
maxSize, "UTF-8", new DefaultFileRenamePolicy());

//5. 파일업로드완료
//5-1. 전달되는 이름, 제목 저장
name = multi.getParameter("name");
subject = multi.getParameter("subject");
//5-2. 전달된 파일의 이름 확인
Enumeration files = multi.getFileNames(); //업캐스팅
String file1 = (String) files.nextElement(); //위의 업캐스팅한 걸 다운캐스팅
//5-3. 서버에 저장되는 파일 이름
filename = multi.getFilesystemName(file1);
System.out.println("filename : "+filename);
//5-4. 원래 파일이름
OFilename = multi.getOriginalFileName(file1);
System.out.println("OFilename : "+OFilename);
//여기까지 업로드 기능 구현 완료! => 6. 파일업로드는 예외가 많이때문에 예외처리를 해야한다
}catch(Exception e){
e.printStackTrace();
}
%>

<!-- 데이터를 전달하기
enctype안적어도 될까? 업로드 유무로 판단!
업로드할꺼면 필요, 업로드하지않을거면 필요하지않다
아래처럼 input type을 text로 그냥 가져가면 중요한 정보들이 유출될 수있다.
보안을 위해 2가지 방법이 있다.
1안은 submit버튼사용 2안은 a태그사용한 방법이다. 편한 걸로하면되지만 보통 2안으로 많이한다
-->

<%-- <form action="fileCheck.jsp" method="post">
<input type="text" name="name" value="<%=name %>" readonly><br>
<input type="text" name="subject" value="<%=subject %>"><br>
<input type="text" name="filename" value="<%=filename %>"><br>
<input type="text" name="ofilename" value="<%=OFilename %>"><br>
<input type="submit" value="확인하기">
</form> --%>

<!-- 1안 : form태그는 보여주지않는 않고 fileCheck에서 출력할 것이므로 hidden으로 type을 지정 후 submit버튼 이용-->
<form action="fileCheck.jsp" method="post">
<input type="hidden" name="name" value="<%=name %>" readonly><br>
<input type="hidden" name="subject" value="<%=subject %>"><br>
<input type="hidden" name="filename" value="<%=filename %>"><br>
<input type="hidden" name="ofilename" value="<%=OFilename %>"><br>
<input type="submit" value="올린 파일 정보하기">
</form>

<!-- 2안 : hidden때문에 생성된 submit버튼 위의 빈칸이 신경쓰이면 a태그를 이용가능.
a태그를 이용하여 post방식 어떻게 데이터를 전달할수있을까? 이때 js를 사용한다 onclick="javascript:filecheck.submit" -->
<form action="fileCheck.jsp" method="post" name="filecheck">
<input type="hidden" name="name" value="<%=name%>"><br>
<input type="hidden" name="subject" value="<%=subject%>"><br>
<input type="hidden" name="filename" value="<%=filename%>"><br>
<input type="hidden" name="ofilename" value="<%=OFilename%>"><br>
</form>
<a href="#" onclick="javascript:filecheck.submit();">업로드 확인 및 다운로드 페이지로 이동</a>

4. fileCheck.jsp생성

  • 사용자가 입력한 정보를 체크하고 파일명을 눌렀을때 다운로드 가능하도록 file_down.jsp로 이동하는 기능 구현
  • 서버에 저장된 파일명의 a태그에서 get방식으로 파라미터로 정보를 전달한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<h2>업로드하지 않고 확인용으로 전달받은 데이터 화면에 출력</h2>
<%
request.setCharacterEncoding("UTF-8");
String name = request.getParameter("name");
String subject = request.getParameter("subject");
String filename = request.getParameter("filename");
String ofilename = request.getParameter("ofilename");
%>


<fieldset>
<legend>업로드 정보 확인용</legend>
작성자명 : <%=name %><br>
제목 : <%=subject %><br>
서버에 저장된 파일명 : <a href="file_down.jsp?file_name=<%=filename %>"> <%=filename %></a><br>
원본파일명 : <%=ofilename %><br>
</fieldset>

5. file_down.jsp 생성

  1. servletContext : 지금 쓰고있는 내 프로젝트 정보.

  2. MIME타입 : 클라이언트에게 전송되는 데이터(문서)를 다양하게 처리 가능하도록 하는 메커니즘
    웹에서 파일의 확장자는 큰 의미가 없음(스트림형태로 데이터가 전달) -> 스트림형태일때는 데이터 구분못함.
    따라서 각 데이터(문서)에서 올바른 형태의 데이터를 전달하도록 MIME 타입을 지정
    브라우저들이 응답정보(리소스)를 받았을때 어떤 형태로 처리해야하는지 판단하는 기준
    마임타입을 가져오는 방법은 getServletContext().getMimeType()으로 가능

  3. 브라우저에 따른 데이터 처리(대응)

    • 왜 인터넷익스플로러를 기준으로 나눌까?

      • 인터넷익스폴러는 다운로드시 한글파일이 깨짐 -> 따라서 파일을 인코딩해서 다운로드함
      • 이때 공백문자가 플러스(+)기호로 나옴 -> 이걸 %20으로 변경
    • 인터넷익스플로러인지 아닌지는 어떻게 구분할까?

      • 접속한 사용자정보(User-Agent)를 불러와서 indexOf사용하여 MSIE이나 Trident이 들어가 있으면 익스플로러이다.
        1
        2
        3
        String agent = request.getHeader("User-Agent");
        System.out.println("agent : "+ agent);
        boolean ieBrowser = agent.indexOf("MSIE") > -1 || agent.indexOf("Trident") > -1;
  4. 데이터다운로드 처리

    • 브라우저가 응답정보를 읽어서(해석) 처리시 “Content-Disposition” 설정값이 “attachment;” 모든 데이터 다운로드 처리한다.

    • 만약 설정안하게되면 어떻게 될까? 브라우저의 기본값에 의해 처리한다.

  5. 데이터 출력(다운로드)

    • flush() : 데이터가 담긴 배열이 꽉 차지 않았을때는 데이터를 출력하지않는다. 이때 flush가 배열의 빈 공간을 공백으로 채워서 데이터 출력되도록 만들어준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<%@page import="java.net.URLEncoder"%>
<%@page import="javax.activation.MimeType"%>
<%@page import="java.io.FileInputStream"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<% //1. 전달받은 데이터 저장
String filename = request.getParameter("file_name");
System.out.println(" 전달된 파일 명 : "+filename);

//2. 파일 다운로드할 위치(= 파일 업로드 위치) =>가상경로
//savepath는 저장되는 폴더명
String savepath = "upload";

//3. 내 프로젝트 정보(ServletContext)안에서 가상경로를 실제경로로 계산해줘야한다.
//3-1. 서버안에 있는 물리적 위치 확인
ServletContext context = getServletContext();
String DownloadPath = context.getRealPath(savepath);
System.out.println("DownloadPath : "+DownloadPath);
//3-2. 다운로드할 파일의 전체 경로
//역슬러시 두개는 이스케이프문자로서 역슬러시 1개만 표현됨
String FilePath = DownloadPath+"\\"+filename;
//여기까지다운로드기능완료

//4. 데이터 응답처리를 다운로드 형태로 구현
//4-1. 파일을 한번에 처리하기위한 배열
byte[] b = new byte[4069];

//4-2. 파일입출력을 처리하는 파일 입력 스트림 객체생성(즉, 파일을 읽어오는 통로만들기)
FileInputStream fis = new FileInputStream(FilePath);

//4-3. 다운로드할 파일의 마임타입 확인(mime)
//=> 클라이언트에게 전송되는 데이터(문서)를 다양하게 처리 가능하도록 하는 메커니즘
String MimeType = getServletContext().getMimeType(FilePath);
System.out.println("Mime type: "+MimeType);

if(MimeType == null){
//기본값 지정 - 이진파일을 처리하기위한 기본타입
//-> 잘 알려지지 않은 파일을 의미하기때문에
//-> 브라우저는 보통 자동실행을 안하고 대화상자를 이용하여 사용자에게 실행할지 질문함
MimeType = "application/octet-stream";
}

//4-4내 응답정보를 마임타입으로 지정
response.setContentType(MimeType);

//5. 브라우저에 따른 데이터 처리(대응)
//왜 인터넷익스플로러를 기준으로 나눌까?
//인터넷익스폴러는 다운로드시 한글파일이 깨짐 -> 따라서 파일을 인코딩해서 다운로드함
//이때 공백문자가 플러스(+)기호로 나옴 -> 이걸 %20(공백)으로 변경

//익스인지 아닌지는 어떻게 알까?
String agent = request.getHeader("User-Agent");
System.out.println("agent : "+ agent);
boolean ieBrowser =
agent.indexOf("MSIE") > -1 || agent.indexOf("Trident") > -1;

if(ieBrowser){ // IE 일때 : +를 %20(공백)으로 변경
filename = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20");
}else{ // IE 아닐때 : 데이터인코딩(한글깨짐방지)
filename = new String(filename.getBytes("UTF-8"), "iso-8859-1");
}

//6. 데이터다운로드 처리
//브라우저가 응답정보를 읽어서(해석) 처리시
//"Content-Disposition" 설정값이 "attachment;" 모든 데이터 다운로드 처리
response.setHeader("Content-Disposition", "attachment; filename="+filename);

//7. 데이터출력(다운로드)
ServletOutputStream out2 = response.getOutputStream();
//7-1. 데이터출력 통로 생성
int data = 0;
while((data = fis.read(b,0,b.length)) != -1){
//데이터출력시 파일이 있을동안 계속 진행
out2.write(b,0,data); //배열에다가 data를 담아서 출력
out2.flush();
}
//8. 자원해제

out2.close();
fis.close();

//9. java.lang.IllegalStateException:
//이 응답을 위해 getOutputStream()이 이미 호출되었습니다.
// => JSP->Servlet 변환시 out 객체가 자동생성, 추가로 OutputStream 객체 생성시
// 오류 메세지 출력( 다운로드 문제 X, 서버에 에러 메세지 로그 쌓임)

out.clear();
out = pageContext.pushBody();
%>

5-1. java.lang.IllegalStateException에러

java.lang.IllegalStateException: 이 응답을 위해 getOutputStream()이 이미 호출되었습니다.

jsp가 실행될때 java파일로 변환되어 실행되고 그 바뀌는 걸 servlet이라고 한다.
이 변환시에 out이라는 객체가 자동 생성된다.
만약 추가로 OutputStream 객체생성시 IllegalStateException에러가 발생한다.

다운로드기능에는 문제가 없으나 에러가 실행될때마다 서버에 에러 메세지 로그 쌓이기때문에 처리해야한다.

1
2
out.clear();
out = pageContext.pushBody();