개발자 미니민의 개발스터디

[JSP/Servlet] 파일처리(파일 업로드) / 서블릿 파일 업로드

by mini_min

JSP

[JSP/Servlet] 파일처리(파일 업로드) / 서블릿 파일 업로드

✔️ 파일 업로드를 위한 <form> 태그

: <form> 태그 안에 정의된 요청 파라미터를 클라이언트에서 서버로 전송할 때 인코딩 방법을 지정한다.

: method 속성이 post 인 경우에만 속성은 유효하다.

 

👩‍💻 속성값

1. application/x-www-form-urlencoded 

: 기본 enctype 속성으로 해당 콘텐츠 타입은 바이너리 데이터(파일 등) 을 전송할 수 없다.

 

2. multipart/form-data

: 문자 인코딩을 하지 않고 전송되며, 파일을 전송할 때 사용한다.

: 여러 형태의 데이터(파일 내용, 텍스트 입력 값) 이 동시에 전달되며, 각 데이터는 [boundary = ....] (경계 정보) 뒤에 붙은 문자열을 이용하여 구분된다.

 

3. text/plan

: 공백이 + 문자로 변환되며 특수 문자를 인코딩 하지 않는다.

 

✔️ enctype 이 기본인 경우

: 파일을 전송해도 파일은 파일 이름만 전송된다. (내용전달x)

<h3>파일 업로드</h3>
<%--
	form 태그에서 enctype 을 생략하면 application/x-www-form-urlencoded (기본)
	- application/x-www-form-urlencoded 은 파라미터를 주소형식으로 인코딩하여 전송한다.
	  파라미터는 "이름1=값1&이름2=값2" 형태로 body 영역에 실어서 보냄
	- application/x-www-form-urlencoded 속성 값에서 파일을 전송하면 파일은 파일 이름만 전송된다.
	- enctype는 method가 post 에서만 유효하다.
--%>

<form action="ex01_ok.jsp" method="post" enctype="application/x-www-form-urlencoded">
<p> 제목 : <input type="text" name="subject"></p>
<p> 파일 : <input type="file" name="selectFile"></p>
<p>
	<button type="submit">확인</button>
</p> 

</form>

 

✔️ enctype이 multipart/form-data 인 경우

: 문자 인코딩 없이 그대로 전달되며, 파일을 전송하는 경우에 사용한다.

: 파일 내용도 함께 전송된다.

: 파라미터는 request.getParameter 로 받을 수 없음

<h3>파일 업로드</h3>
<%--
	form 태그에서 enctype을 multipart/form-data 로 준 경우
	: 문자를 인코딩하지 않고 그대로 전송하며, 파일을 전송해야 하는 경우에 사용한다.
	: 파일 내용도 같이 전송
	
	- enctype는 method가 post 에서만 유효하다.
--%>

<form action="ex02_ok.jsp" method="post" enctype="multipart/form-data">
<p> 제목 : <input type="text" name="subject"></p>
<p> 파일 : <input type="file" name="selectFile"></p>
<p>
	<button type="submit">확인</button>
</p> 

</form>

 

- 요청 받은 정보 출력하기 ✨

: 웹과 JSP 에서 enctype은 contenttype 이다.

결과 : multipart/form-data; boundary=----WebKitFormBoundarylrnP16LoJSIqSN1I

 

- 헤더정보

: 헤더정보는 Enumeration 클래스를 통해 출력할 수 있다.

 

- request body 영역으로 넘어온 데이터도 출력 가능하다.

: inputStream 이용하여 데이터 정보를 출력한다.

<h3> 요청 받은 정보 </h3>

<%
	//웹, jsp 에서 enctype 은 contenttype이다.
	String contentType = request.getContentType();
	out.print("<p>contentType(enctype) : " + contentType + "</p>");
	out.print("<hr>");
	
	out.print("<h3>[헤더 정보]...</h3>");
	Enumeration<String> e = request.getHeaderNames();
	while(e.hasMoreElements()){
		String name = e.nextElement();
		String value = request.getHeader(name);
		out.print("<p>" + name + " : " + value + "</p>");
	}
	
	out.print("<hr>");
	
	out.print("<h3>[request body 영역으로 넘어온 데이터]...</h3>");
	
	InputStream is = request.getInputStream();
	byte[] buf = new byte[2048];
	int size;
	String str;
	while((size = is.read(buf))!= -1){
		str = new String(buf, 0, size, "utf-8");
		out.print("<p>" + str + "</p>");
	}
	out.print("<hr>");
	

%>

 

✔️ cos.jar 을 이용한 파일 업로드.

: cos.jar 을 이용해서 파일을 업로드할 수 있다.

MultipartRequest mreq = new MultipartRequest(request, pathname, maxSize, encType,
			new DefaultFileRenamePolicy());
<%
request.setCharacterEncoding("utf-8");

// /study3 경로의 실제 경로(로컬컴퓨터의 위치) 
String root = session.getServletContext().getRealPath("/");
String pathname = root + "uploads" + File.separator + "pds";
File f= new File(pathname);
if( ! f.exists()){	//폴더가 없으면 폴더 만들어라는 뜻
    f.mkdirs();
}

String encType = "utf-8";
int maxSize = 5 * 1024 * 1024; // 5MB

//cos.jar 을 이용한 파일 업로드
                    //request, 파일저장위치, 최대파일크기, 파라미터인코딩, 중복파일보호
MultipartRequest mreq = new MultipartRequest(request, pathname, maxSize, encType,
        new DefaultFileRenamePolicy());

// 제목
String subject = mreq.getParameter("subject");

//서버에 저장된 파일 이름
String saveFilename = mreq.getFilesystemName("selectFile");

//클라이언트가 올린 파일 이름
String originalFilename = mreq.getOriginalFileName("selectFile");

//파일크기
long fileSize = 0;
File file = mreq.getFile("selectFile");
if(file != null){
    fileSize = file.length();
}


%>
<form action="ex03_ok.jsp" method="post" enctype="multipart/form-data">
<p> 제목 : <input type="text" name="subject"></p>
<p> 파일 : <input type="file" name="selectFile"></p>
<p>
	<button type="submit">확인</button>
</p> 

</form>

 

 

✔️ 서블릿 3.0 에서의 파일 업로드

: 서블릿 3.0부터는 파일을 업로드 하기 위해 multipart/form-data Content Type 으로 요청된 정보는 다른 API 를 사용하지 않고 파일을 업로드하는 방법을 지원한다.

: HttpServletRequest 는 multipart/form-data Content Type 으로 요청된 모든 Part 를 가져오는 메소드를 제공한다.

 

📓 Part 관련 메소드

: getParts() : 업로드된 모든 Part 를 Collection 으로 반환한다.

: getPart(String name) : 저장된 이름의 Part를 반환하고, 없으면 null을 반환한다.

 

📓 Part 인터페이스의 주요 메소드

: getName() : 이 Part 에 해당하는 multipart 폼 양식의 필드 이름을 반환한다.

: getContentType() : 브라우저가 전달한 Content type을 반환한다. 정의되지 않았으면 null 을 반환한다.

: getInputStream() : 이 Part의 InputStream 객체를 반환한다.

: getHeaderName() : 이 Part 에 제공된 모든 헤더의 이름을 Collection 으로 반환한다.

: getSize() : 이 Part 의 크기를 반환한다.

: write(String fileName) : 업로드된 파일을 디스크에 저장한다.

: delete() : 이 Part에 관련된 임시 파일 및 Part의 기본 저장소를 지운다.

 

👩‍💻 @MultipartConfig 어노테이션

: 서블릿의 인스턴스가 multipart/form-data MIME 형식의 요청을 처리하기 위한 설정을 구성하는 어노테이션이다.

 

📓 속성

- fileSizeThreshold : 파일이 디스크에 기록될 때까지의 크기 임계 값이다. 값의 단위는 바이트

- location : 파일이 기본적으로 저장될 디렉토리이며 기본값은 ""

- maxFileSize : 파일을 업로드할 수 있는 최대 크기. 단위는 바이트. 기본값은 무한을 의미하는 -1L

- maxRequestSize : multipart/form-data 요청에 허용되는 최대 크기이며 기본값은 무한을 의미하는 -1L

 

- form 양식

<h3> 서블릿 3.0이상 을 이용한 파일 </h3>

<form action="${pageContext.request.contextPath}/upload/write_ok.do" 
	method="post" enctype="multipart/form-data">
	
	<p> 제목 : <input type="text" name="subject"></p>
	<p> 파일 : <input type="file" name="selectFile"></p>
	<p>
		<button type="submit">확인</button>
	</p>

</form>

 

- 서블릿 (TestServlet)

: @MultipartConfig 어노테이션을 사용하면, 파일 이외의 파라미터는 request.getParameter 사용 가능

: 하나의 파라미터 당 Part 객체 하나가 생성된다.

: Part 객체 얻는 방법 : Part p = req.getPart("이름");

: 모든 Part 객체 반환은 Collection + getParts();

/*
 * @MultipartConfig
 	- 서블릿 3.0 부터 파일 업로드를 지원하는 어노테이션
 	- @MultipartConfig 애노테이션을 사용하면 enctype= "multipart/form-data" 로 넘어온
 		파일 이외의 파라미터는 req.getParameter("이름")으로 넘겨 받을 수 있다!!(없으면안됨)
 	- 하나의 파라미터당 하나의 Part 객체가 만들어 진다.
 		예를 들어, "이름, 제목, selectFile 이름으로 파일 두개"를 전송하면
 		Part 객체는 4개 만들어진다.
 	- Part 객체의 메소드를 이용하여 간단히 파일 업로드가 가능하다.
 	- Part 객체는 다음과 같이 얻는다.
 		Part p = req.getPart("이름");
 	- 모든 Part 객체 반환
 		Collection<Part> pp = req.getParts();
 */

 

- dopost, doget, forward, execute 생략...

: if(! f.exists()) : 해당되는 이름의 파일이 없으면 생성하기

protected void form(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    forward(req, resp, "/WEB-INF/views/upload/write.jsp");
}

protected void submit(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    HttpSession session = req.getSession();
    String root = session.getServletContext().getRealPath("/");
    String pathname = root + "uploads" + File.separator + "pds";
    File f = new File(pathname);

    if(! f.exists()) {
        f.mkdirs();
    }

    //제목 받기
    String subject = req.getParameter("subject");

    //파일 받기
    Part p = req.getPart("selectFile");
    String originalFilename = getOriginalFilename(p);
    long fileSize = 0;
    String saveFilename = null;
    //이름 같은 파일들이 있으면 덮어버림
    if(originalFilename != null) { //서버에 올라온 파일이 있으면
        //중복 만나지않고 서버에 저장하는 방법 생각
        //확장자(.확장자)
        String fileExt = originalFilename.substring(originalFilename.lastIndexOf("."));

        //서버에 저장할 파일이름 만들기
        saveFilename = String.format("%1$tY%1$tm%1$td%1$tH%1$tM%1$tS", Calendar.getInstance());
        saveFilename += System.nanoTime();
        saveFilename += fileExt;

        //서버에 파일 저장하기
        String path = pathname + File.separator + saveFilename;
        p.write(path);

        fileSize = p.getSize();

    }

    req.setAttribute("subject", subject);
    req.setAttribute("pathname", pathname);
    req.setAttribute("originalFilename", originalFilename);
    req.setAttribute("saveFilename", saveFilename);
    req.setAttribute("fileSize", fileSize);

    forward(req, resp, "/WEB-INF/views/upload/result.jsp");
}


//클라이언트가 올린 파일 이름
private String getOriginalFilename(Part p) {
    String []ss = p.getHeader("content-disposition").split(";");
    for(String s : ss) {
        if(s.trim().startsWith("filename")) {
            return s.substring(s.indexOf("=")+1).trim().replace("\"", "");
        }
    }
    return null;
}

 

 

 

블로그의 정보

개발자 미니민의 개발로그

mini_min

활동하기