@Override
public String requestHandler(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String UPLOAD_DIR = "file_repo";
String uploadPath = request.getServletContext().getRealPath("") + File.separator + UPLOAD_DIR;
// 실제 파일 경로 객체 생성
File currentDirPath = new File(uploadPath);
// 디렉터리가 존재하지 않으면 생성
if (!currentDirPath.exists()) {
currentDirPath.mkdir();
}
// 파일 업로드 관련 설정
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(currentDirPath);
factory.setSizeThreshold(1024 * 1024);
String fileName = null;
ServletFileUpload upload = new ServletFileUpload(factory);
try {
// 업로드된 항목 처리
List<FileItem> items = upload.parseRequest(request);
for (FileItem fileItem : items) {
if (fileItem.isFormField()) {
System.out.println(fileItem.getFieldName() + "=" + fileItem.getString("utf-8"));
} else { // 파일이면
if (fileItem.getSize() > 0) {
// 파일 이름 추출
int idx = fileItem.getName().lastIndexOf(File.separator); // 운영체제에 맞는 경로 구분자 사용
if (idx == -1) {
idx = fileItem.getName().lastIndexOf("/"); // 리눅스에서 경로 구분자 처리
}
// 파일 이름만 추출
fileName = fileItem.getName().substring(idx + 1);
File uploadFile = new File(currentDirPath + File.separator + fileName);
// 파일 중복 체크
if (uploadFile.exists()) {
fileName = System.currentTimeMillis() + "_" + fileName;
uploadFile = new File(currentDirPath + File.separator + fileName);
}
// 파일 저장
fileItem.write(uploadFile);
}
}
}
} catch (Exception e) {
e.printStackTrace();
// 오류 발생 시 사용자에게 알림을 위한 응답 처리
response.setContentType("text/html;charset=euc-kr");
response.getWriter().print("error"); // 파일 업로드 오류 시
return null;
}
// 업로드된 파일 이름을 응답으로 반환
if (fileName != null && !fileName.isEmpty()) {
response.setContentType("text/html;charset=euc-kr");
response.getWriter().print(fileName); // 파일 이름을 클라이언트로 반환
} else {
response.setContentType("text/html;charset=euc-kr");
response.getWriter().print("error"); // 파일 업로드 실패 시
}
return null;
}
1. 파일 업로드 디렉터리 설정
String UPLOAD_DIR = "file_repo";
String uploadPath = request.getServletContext().getRealPath("") + File.separator + UPLOAD_DIR;
- UPLOAD_DIR는 업로드된 파일들이 저장될 디렉토리의 이름입니다.
- getServletContext().getRealPath("")는 웹 애플리케이션의 루트 디렉토리 경로를 반환합니다. 이 경로 뒤에 file_repo 폴더를 붙여서 실제 파일 저장 경로를 설정합니다.
- File.separator는 운영 체제에 맞는 경로 구분자(\ 또는 /)를 자동으로 사용합니다.
2. 파일 업로드 경로가 존재하지 않으면 디렉터리 생성
File currentDirPath = new File(uploadPath);
if (!currentDirPath.exists()) {
currentDirPath.mkdir();
}
- uploadPath로 지정된 경로가 실제로 존재하는지 확인합니다.
- 만약 경로가 존재하지 않으면, mkdir() 메서드를 통해 해당 디렉터리를 새로 생성합니다.
3. 파일 업로드 설정
DiskFileItemFactory factory = new DiskFileItemFactory();
factory.setRepository(currentDirPath);
factory.setSizeThreshold(1024 * 1024);
- DiskFileItemFactory는 파일 업로드 시 파일 항목을 처리하기 위한 공장 클래스입니다.
- setRepository(currentDirPath)는 파일을 업로드할 때 임시로 저장할 디렉터리를 지정합니다. 이 디렉터리는 업로드된 파일을 메모리로 처리하기 전에 임시로 저장할 위치입니다.
- setSizeThreshold(1024 * 1024)는 임시 저장할 수 있는 최대 파일 크기(여기서는 1MB)입니다. 이 크기를 초과하는 파일은 실제 파일 시스템에 저장됩니다.
4. 파일 업로드 처리
ServletFileUpload upload = new ServletFileUpload(factory);
List<FileItem> items = upload.parseRequest(request);
- ServletFileUpload는 DiskFileItemFactory를 이용하여 파일 업로드를 처리하는 클래스입니다.
- parseRequest(request)는 HTTP 요청에서 파일 데이터를 파싱하여 FileItem 객체 리스트를 반환합니다. 이 리스트에는 파일과 폼 필드 데이터가 모두 포함됩니다.
5. 파일 항목 처리
for (FileItem fileItem : items) {
if (fileItem.isFormField()) {
System.out.println(fileItem.getFieldName() + "=" + fileItem.getString("utf-8"));
} else { // 파일이면
if (fileItem.getSize() > 0) {
// 파일 이름 추출
int idx = fileItem.getName().lastIndexOf(File.separator);
if (idx == -1) {
idx = fileItem.getName().lastIndexOf("/");
}
fileName = fileItem.getName().substring(idx + 1);
File uploadFile = new File(currentDirPath + File.separator + fileName);
// 파일 중복 체크
if (uploadFile.exists()) {
fileName = System.currentTimeMillis() + "_" + fileName;
uploadFile = new File(currentDirPath + File.separator + fileName);
}
// 파일 저장
fileItem.write(uploadFile);
}
}
}
- for 루프는 items 리스트를 순회하면서 각 항목이 폼 필드인지 파일인지 확인합니다.
- 폼 필드일 경우 isFormField()가 true로 반환되며, 이 경우 파일 외의 다른 데이터를 처리합니다.
- 파일일 경우, isFormField()가 false이고, 파일이 실제로 선택되었으면(fileItem.getSize() > 0), 파일을 처리합니다.
- 파일 이름 추출:
- fileItem.getName()은 파일의 전체 경로를 반환하는데, 이를 분리하여 파일 이름만 추출합니다.
- lastIndexOf(File.separator)와 lastIndexOf("/")를 사용하여 경로에서 파일 이름만 추출합니다. (File.separator를 사용하면 운영체제에 맞는 경로 구분자(\ 또는 /)를 처리합니다.)
- 파일 중복 처리:
- 동일한 이름의 파일이 이미 존재하는지 확인하고, 존재할 경우 파일 이름에 현재 시간을 덧붙여 중복을 피합니다.
- 파일 저장:
- fileItem.write(uploadFile)로 업로드된 파일을 지정된 경로(uploadFile)에 저장합니다.
6. 예외 처리
} catch (Exception e) {
e.printStackTrace();
response.setContentType("text/html;charset=euc-kr");
response.getWriter().print("error"); // 파일 업로드 오류 시
return null;
}
- try-catch 블록을 사용하여 예외가 발생하면 이를 처리합니다.
- 예외가 발생한 경우, e.printStackTrace()로 예외를 출력하고, 클라이언트에게 "error" 메시지를 반환합니다.
- 파일 업로드 도중 발생할 수 있는 오류(파일 시스템 오류, 파싱 오류 등)를 처리합니다.
7. 업로드된 파일 이름 반환
if (fileName != null && !fileName.isEmpty()) {
response.setContentType("text/html;charset=euc-kr");
response.getWriter().print(fileName); // 파일 이름을 클라이언트로 반환
} else {
response.setContentType("text/html;charset=euc-kr");
response.getWriter().print("error"); // 파일 업로드 실패 시
}
- 파일 업로드가 성공적으로 완료되면, 업로드된 파일의 이름을 클라이언트에게 응답으로 반환합니다.
- 만약 파일 이름이 null이거나 비어 있다면 업로드가 실패했으므로 "error" 메시지를 반환합니다.
8. 최종 반환값
return null;
- requestHandler 메서드는 최종적으로 null을 반환합니다. 이는 Servlet이나 Controller에서 다른 페이지로 포워딩하거나 리다이렉트하는 로직이 없다면 문제가 되지 않지만, 페이지 이동이 필요하다면 다른 반환값을 처리할 수도 있습니다.
필요한 JAR 파일
- commons-fileupload.jar:
- 이 JAR 파일은 파일 업로드를 처리하는 핵심적인 라이브러리입니다. ServletFileUpload와 DiskFileItemFactory와 같은 클래스를 제공하여 파일 업로드 기능을 쉽게 구현할 수 있습니다.
- commons-io.jar:
- 이 JAR 파일은 commons-fileupload 라이브러리가 의존하는 파일 입출력 관련 유틸리티를 제공합니다. 예를 들어, 파일 시스템 경로 관리 등에서 유용한 메서드를 포함하고 있습니다.
Maven의 경우
<dependencies>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version> <!-- 최신 버전 확인 필요 -->
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.7</version> <!-- 최신 버전 확인 필요 -->
</dependency>
</dependencies>
1. HTML 부분: 파일 입력 필드
<div class="form-group">
<label class="control-label col-sm-2" for="">첨부 파일:</label>
<div class="col-sm-10">
<input type="file" class="form-Lebel" id="file" name="file">
</div>
</div>
<input type="hidden" name="filename" id="filename" value=""><!-- file이름이 하나 넘어가야하기 때문에 hidden으로 해둠 -->
- 파일 입력 필드 (<input type="file" id="file" name="file">):
- 사용자가 파일을 선택할 수 있도록 하는 HTML input 요소입니다.
- id="file": 자바스크립트에서 이 파일 입력 필드를 쉽게 선택할 수 있도록 고유한 ID 값을 지정합니다.
- name="file": 서버에 전송할 때 이 파일을 나타내는 키 이름입니다. 파일을 서버로 전송할 때 이 이름을 사용하여 파일을 처리합니다.
- 숨겨진 입력 필드 (<input type="hidden" name="filename" id="filename" value="">):
- 파일 업로드 후, 실제 파일 이름을 저장하는 숨겨진 입력 필드입니다. 파일 이름을 서버로 전송할 때 사용됩니다. AJAX 요청을 통해 파일을 업로드하고 그 결과로 받은 파일 이름을 이 필드에 저장한 후, 폼을 제출합니다.
2. JavaScript: 파일 업로드 로직
function uploadFile(){
if($("#file").val() != '') { // 파일이 첨부된 경우
var formData = new FormData(); // formData를 생성할 수 있음
formData.append("file", $("input[name=file]")[0].files[0]); // 첫번째 file을 가지고와 file이라는 이름으로 formData에 넣어줌
$.ajax({
url : "<c:url value='/fileAdd.do'/>", // formData를 전송해야 함
type : "post",
data : formData,
processData : false, // formData이라는 하나의 바이너리 데이터가 넘어간다. 이 경우 이렇게 값을 설정
contentType : false, // contentType도 false값을 주어야 한다.
success : function(data) { // 업로드된 실제 파일 이름을 전달받기
$('#filename').val(data); // 숨겨진 파일 이름 필드에 파일 이름 저장
document.form1.action = "<c:url value='/memberInsert.do'/>"; // 텍스트 데이터를 저장하는 부분
document.form.submit(); // 폼을 제출
},
error : function() {
alert("error");
}
});
} else { // 파일이 첨부되지 않은 경우
// 파일이 없을 때 처리할 로직
}
}
설명:
- 파일이 첨부된 경우:
- $("#file").val()을 사용하여 파일 입력 필드에 파일이 선택되었는지 확인합니다. 선택된 파일이 있으면 FormData 객체를 생성하여 파일을 담습니다.
- formData.append("file", $("input[name=file]")[0].files[0]):
- FormData 객체는 폼 데이터를 제어하는 객체로, 파일과 같은 바이너리 데이터를 전송할 수 있게 도와줍니다.
- $("input[name=file]")[0].files[0]은 파일 입력 필드에서 사용자가 선택한 첫 번째 파일을 가져옵니다.
- AJAX 요청:
- $.ajax()를 사용하여 비동기적으로 서버에 파일을 전송합니다.
- url에는 파일을 처리할 서버의 URL인 "/fileAdd.do"를 지정합니다.
- type: "post"로 POST 방식으로 요청을 보냅니다.
- data: formData로 FormData 객체를 전송합니다. 이 객체는 파일 데이터를 포함하고 있습니다.
- processData: false와 contentType: false는 FormData 객체를 사용할 때 필수 설정입니다. 이 설정들이 없으면 파일이 제대로 전송되지 않습니다.
- 서버로부터 응답받은 후:
- success 콜백 함수에서 서버에서 반환한 데이터를 받습니다. 여기서 data는 서버에서 처리된 파일 이름일 가능성이 높습니다.
- 이 파일 이름을 숨겨진 필드인 #filename에 저장하고, 이후 document.form.submit()을 호출하여 회원가입 폼을 제출합니다. 이때 숨겨진 파일 이름 필드도 함께 제출됩니다.
- 파일이 첨부되지 않은 경우:
- 파일이 첨부되지 않은 경우, 아무런 처리가 이루어지지 않도록 되어 있습니다. 이 부분에 추가적인 로직을 넣어 파일이 없어도 폼을 제출하도록 처리할 수 있습니다.