고급JAVA 32강 - File 업로드

fileUploadTest.jsp

파일 업로드 테스트

  • Servlet 3.0이상에서 파일 업로드를 처리하려면 서블릿에 @MultipartConfig라는 애노테이션을 설정해야 한다.
  • MultipartConfig 애노테이션 설정 방법(사용되는 변수 및 역할)
    1) location : 업로드한 파일이 임시적으로 저장될 경로를 지정한다.
    (기본값 : "")
    2) fileSizeThreshold : 이 속성에서 지정한 크기보다 큰 파일은
    위의 location에서 지정한 디스크의 임시 디렉토리에 저장한다.
    (단위 : byte, 기본값 : 0 (무조건 임시 디렉토리를 사용한다.))
    3) maxFileSize : 1개의 파일의 최대 크기 (단위: byte, 기본값: -1L(무제한))
    4) maxRequestSize : 서버로 전송되는 Request 데이터의 최대 크기
    (단위 : byte, 기본값 : -1L(무제한))

fileSizeThreshold = 1024 1024 10 => 10Mb를 의미
10Mb가 넘어가면 임시폴더가 아닌 저장경로에 저장됨
maxRequestSize : 요구되는 전체 데이터 크기

=> 파일 업로드는 get방식은 사용 불가

각각의 데이터가 저장되는 공간을 part라고 함
part가 여러개인 데이터를 전송한다 = multipart
각각의 part별로 처리할 수 있는 방법이 있어야 함

part가 몇 개가 오는지 모르니까 전체 part에서 하나씩 검사해서 file데이터를 찾아내야 함.
하나의 part가 file 데이터면 file 정보를 가져오기 위한 작업을 해야함.

servlet.http에 있는 Part로 import

content-disposition => part를 구분하는 header

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>File Upload 연습</h2>
<!-- enctype="multipart/form-data" ==> 파일 전송 시 반드시 필요한 속성 -->
<form method="post" enctype="multipart/form-data" action="<%=request.getContextPath()%>/fileUploadTest.do">
	이름 :<input type="text" name="username"><br><br>
	파일 1개 선택 : <input type="file" name="uploadFile1"><br><br>
	다중파일 선택 : <input type="file" name="uploadFile2" multiple><br><br>
	<button type="submit">자료 전송</button>
</form>
<br><hr><br>
<a href="<%=request.getContextPath()%>/uploadFileList.do">업로드된 전체 파일 목록 가져오기</a>

</body>
</html>

FileVO

package kr.or.ddit.basic.vo;

public class FileVO {
	private String fileName;	// 파일 이름
	private long fileSize;		// 파일 크기
	private String uploadStatus;// Upload성공 여부
	
	public String getFileName() {
		return fileName;
	}
	public void setFileName(String fileName) {
		this.fileName = fileName;
	}
	public long getFileSize() {
		return fileSize;
	}
	public void setFileSize(long fileSize) {
		this.fileSize = fileSize;
	}
	public String getUploadStatus() {
		return uploadStatus;
	}
	public void setUploadStatus(String uploadStatus) {
		this.uploadStatus = uploadStatus;
	}
	
	
	
}

FileUploadTest.java

package kr.or.ddit.fileupload;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;

import kr.or.ddit.basic.vo.FileVO;

/*
	- Servlet 3.0이상에서 파일 업로드를 처리하려면 서블릿에 @MultipartConfig라는 애노테이션을 설정해야 한다.
	
	- MultipartConfig 애노테이션 설정 방법(사용되는 변수 및 역할)
	  1) location : 업로드한 파일이 임시적으로 저장될 경로를 지정한다. - 윈도우는 temp폴더
	  				(기본값 : "")
	  2) fileSizeThreshold : 이 속성에서 지정한 크기보다 큰 파일은 
	  		위의 location에서 지정한 디스크의 임시 디렉토리에 저장한다.
	  		(단위 : byte, 기본값 : 0 (무조건 임시 디렉토리를 사용한다.))
	  3) maxFileSize : 1개의 파일의 최대 크기 (단위: byte, 기본값: -1L(무제한))		
	  4) maxRequestSize : 서버로 전송되는 Request 데이터의 최대 크기
	  		(단위 : byte, 기본값 : -1L(무제한))
	  		
*/
@WebServlet("/fileUploadTest.do")
@MultipartConfig(
		fileSizeThreshold = 1024 * 1024 * 10,
		maxFileSize = 1024 * 1024 * 30, maxRequestSize = 1024 * 1024 * 50
		
)

public class FileUploadTest extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// upload한 파일이 저장될 폴더 설정
		String uploadPath = "D:/D_Other/uploadFiles";
		
		// 저장될 폴더가 없으면 새로 생성한다.
		File fileUploadDir = new File(uploadPath);
		if(!fileUploadDir.exists()) {
			fileUploadDir.mkdirs();
		}
		
		// 파일이 아닌 일반 데이터는 getParameter()메서드나 getParameterValues()메서드를 이용해서 구한다.
		request.setCharacterEncoding("utf-8");
		String userName = request.getParameter("username");
		System.out.println("일반 파라미터 데이터(username) : " + userName);
		
		//----------------------------------------------------------
		String fileName = ""; // 파일명이 저장될 변수 선언
		
		// 업로드한 파일 정보가 저장될 List 객체 생성
		List<FileVO> fileList = new ArrayList<FileVO>();
		
		/*
		- Servlet 3.0 이상에 새롭게 추가된 Upload용 메서드
		1) request.getParts(); ==> 전체 Part객체가 저장된 컬렉션을 반환한다.
		2) request.getPart("Part이름") ==> 지정된 이름을 가진 part 객체를 반환한다.(특정한 part를 찾을 때)
		*/
		for(Part part : request.getParts()) {
			// 파일명 구하기
			fileName = extractFileName(part);
			
			// 파일명이 공백("")이면 이것은 파일이 아닌 일반 파라미터라는 의미이다.
			if(!("".equals(fileName))) {
				// 1개의 업로드 파일에 대한 정보를 저장할 객체 생성
				FileVO fileVo = new FileVO();
				
				fileVo.setFileName(fileName);
				fileVo.setFileSize((int)Math.ceil(part.getSize()/ 1024.0));
				
				// 파일 저장 ==> Part 객체의 write()메서드 이용
				try {
					part.write(uploadPath + File.separator + fileName);
					fileVo.setUploadStatus("Sucess");
					
					
				} catch (IOException e) {
					fileVo.setUploadStatus("Fail : " + e.getMessage());
				}
				
				// 처리된 파일 정보를 저장한 객체를 List에 추가
				fileList.add(fileVo);
			}
		}//for문 끝...
		
		request.setAttribute("userName", userName);
		request.setAttribute("uploadFileList", fileList);
		
		RequestDispatcher rd = request.getRequestDispatcher("/fileupload/uploadFiles.jsp");
		rd.forward(request, response);
		
	} // doPost()메서드 끝...
	
	/*
	- Part 구조
	1) 파일이 아닐 때 (즉, 일반 파라미터 데이터일 경우)
	------------very1867456sdfsd32156421		==> Part를 구분하는 구분선
	content-disposition: form-data; name="username"		==> 파라미터명
												==> 빈 줄
	tester										==> 파라미터 값 											
	*/
	
	/*
	2) 파일일 경우
	------------very1867456sdfsd32156421		==> Part를 구분하는 구분선
	content-disposition: form-data; name="uploadFile1"; filename="test1.txt"	==> 파일정보
	content-Type: text/plain			==> 파일 종류
										==> 빈 줄
	asdc123456!@#						==> 파일의 내용				
	
	*/
	// Part영역에서 읽어올 '파일명'을 찾아 반환하는 메서드 
	private String extractFileName(Part part) {
		String fileName = "";
		String contentDisposition = part.getHeader("content-disposition");
		// form-data; name="username" 	==> 파일이 아닐 때
		// form-data; name="uploadFile1"; filename="test1.txt";	==> 파일일 때
		
		
		String[] items = contentDisposition.split(";");
		for(String item : items) {
			if(item.trim().startsWith("filename")) {
				// filename="test1.txt"	==> item변수값
				fileName = item.substring(item.indexOf("=") + 2, item.length() - 1);
			}
		}
		
		//file이 아닐 경우 => ""이 반환됨, file일 경우 => file명이 반환됨
		
		return fileName;
	}

}

UploadFileList.java

package kr.or.ddit.fileupload;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import kr.or.ddit.basic.vo.FileVO;

/**
 * Servlet implementation class UploadFileList
 */
@WebServlet("/uploadFileList.do")
public class UploadFileList extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// Upload된 전체 파일 목록을 구성하는 서블릿
		String uploadPath = "D:/D_Other/uploadFiles";
		
		// 저장될 폴더가 없으면 새로 생성한다.
		File fileUploadDir = new File(uploadPath);
		if(!fileUploadDir.exists()) {
			fileUploadDir.mkdirs();
		}
		
		// 파일이 저장된 폴더에서 전체 파일목록을 구해와서 List에 담아준다.
		File[] allFiles = fileUploadDir.listFiles();
		List<FileVO> fileList = new ArrayList<FileVO>();
		
		for(File file : allFiles) {
			if(file.isFile()) {
				FileVO fvo = new FileVO();
				fvo.setFileName(file.getName());
				fvo.setFileSize((int)Math.ceil(file.length() / 1024.0));
				fvo.setUploadStatus("Success");
				fileList.add(fvo);
			}
		}
		
		request.setAttribute("uploadFileList", fileList);
		
		RequestDispatcher rd = request.getRequestDispatcher("/fileupload/uploadFiles.jsp");
		rd.forward(request, response);
		
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

uploadFiles.jsp

<%@page import="kr.or.ddit.basic.vo.FileVO"%>
<%@page import="java.util.List"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Servlet File Upload</title>
</head>
<body>
<%
	String userName = (String)request.getAttribute("userName");
	List<FileVO> fileList = (List<FileVO>)request.getAttribute("uploadFileList");
%>

<%
	if(userName!=null){
%>

	<h2><%=userName %>님이 방금 업로드 한 파일 목록</h2>

<%
	}else{
%>
	<h2>전체 업로드 파일 목록</h2>
<%
	}
%>

<table border='1'>
	<tr>
		<td>파일이름</td><td>파일크기</td><td>업로드 상태</td><td>비고</td>
	</tr>

<%
	if(fileList!=null){
		for(FileVO fileVo : fileList){
%>
	<tr>
		<td><%=fileVo.getFileName() %></td>
		<td><%=fileVo.getFileSize() %></td>
		<td><%=fileVo.getUploadStatus() %></td>
		<td><a href="<%=request.getContextPath()%>/fileDownload.do?filename=<%=fileVo.getFileName()%>">Download</a></td>
	</tr>
<%			
		}
	}
%>

</table>
<br><hr><br>
<a href="<%=request.getContextPath()%>/fileupload/fileUploadTest.jsp">파일 업로드 시작문서로 가기</a>
</body>
</html>

FileDownload.java

package kr.or.ddit.fileupload;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/fileDownload.do")
public class FileDownload extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("utf-8");
		// 파라미터로 넘어오는 filename 구하기
		String fileName = request.getParameter("filename");
		
		String downLoadPath = "d:/d_other/uploadFiles";
		String filePath = downLoadPath + File.separator + fileName;
		
		File file = new File(filePath);
		
		OutputStream out = null;
		FileInputStream fin = null;
		
		if(file.exists()) { // 파일이 있을 때...
			// ContentType 설정
			response.setContentType("application/octet-stream; charset=utf-8"); //파일 다운로드 할 때 octet-stream가 들어가야 함
			
			//response객체의 content-disposition헤더 속성 설정하기
			String headerKey = "Content-Disposition";
			
			// 한글 getFileNameEncoding 메서드 사용
			String downFileName = getFilenameEncoding(fileName, getBrowser(request));
			
			String headerValue = "attachment; filename=\"" + downFileName + "\";";
			response.setHeader(headerKey, headerValue);
			
			getBrowser(request); // 한글이 어떻게 나오는지 검사
			
			try {
				// 출력용 스트림 객체 생성 ==> response객체 이용
				out = response.getOutputStream();
				
				// 파일 입력용 스트림 객체 생성
				fin = new FileInputStream(file);
				byte[] buffer = new byte[1024 * 100]; // 한번에 100KB씩 읽어오라는 뜻
				
				int len = 0;
				
				// byte배열을 이용하여 파일 내용을 읽어와 출력용 스트림을 이용하여 클라이언트에게 전송한다.
				while((len = fin.read(buffer)) > 0) {
					out.write(buffer, 0, len);
				}
				
			} catch (IOException e) {
				System.out.println("입출력 오류 : " + e.getMessage());
			}finally {
				if(fin!=null)try {fin.close();} catch(Exception e) {}
				if(out!=null)try {out.close();} catch(Exception e) {}
			}
			
		}else { // 파일이 없을 때
			response.setCharacterEncoding("utf-8");
			response.setContentType("text/html; charset=utf-8");
			response.getWriter().println("<h3>" + fileName + "파일은 존재하지 않습니다.</h3>");
			
		}
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}
	
	// 사용자의 브라우저 알아내기
	private String getBrowser(HttpServletRequest request) {
		String header = request.getHeader("User-Agent");
		//System.out.println(header);
		if(header.indexOf("MSIE") > -1) {
			return "MSIE";
		}else if(header.indexOf("Chrome") > -1){
			return "Chrome";
		}else if(header.indexOf("Opera") > -1) {
			return "Opera";
		}else if(header.indexOf("Trident/7.0") > -1) { // IE 11이상
			return "MSIE";
		}
		
		
		return "Firefox";
	}
	
	// 브라우저별로 한글 파일명을 인코딩하는 메서드
	private String getFilenameEncoding(String filename, String browser) {
		String encodedFilename = null;
		try {
			if(browser.equals("MSIE")) {
				encodedFilename = URLEncoder.encode(filename, "utf-8").replaceAll("\\+", "%20");
			}else if(browser.equals("Firefox")) {
				encodedFilename = "\"" + new String(filename.getBytes("utf-8"), "8859_1") + "\"";
			}else if(browser.equals("Opera")) {
				encodedFilename = "\"" + new String(filename.getBytes("utf-8"), "8859_1") + "\"";
			}else if(browser.equals("Chrome")) {
				StringBuffer sb = new StringBuffer();
				
				for(int i = 0; i <filename.length(); i++) {
					char c = filename.charAt(i);
					
					if(c> '-') {
						sb.append(URLEncoder.encode(""+c, "utf-8"));
					}else {
						sb.append(c);
					}
				}
				
				encodedFilename = sb.toString();
			}else {
				throw new RuntimeException("지원하지 않는 브라우저입니다.");
			}
			
		} catch (Exception e) {
			// TODO: handle exception
		}
		
		
		return encodedFilename;
	}

}

좋은 웹페이지 즐겨찾기