일단 jsp에서 파일 업로드를 선택하는 form에서 entype이 multipart/form-data로 보내야 한다. 그렇지 않으면 받는쪽에서 파일을 받을 수가 없다. 파일이 없으면 보통의 form은 entype을 바꾸어 주지 않아도 된다.
<form name="writeForm" method="post" action="writeProc.jsp" enctype="multipart/form-data" >
<input type="file" name="attachFile" size="40" />
</form>
이제 받아야 하는데 이걸 받는 역할을 cos.jar가 한다. 폼전송을 multipart/form-data로 보냈기 때문에 기존에 폼을 받던 request.getParameter()로는 받을 수가 없다. 그래서 cos.jar가 파일도 받고 폼의 다른 값들도 다 받아주는 역할을 한다.
cos는 com.oreilly.servlet의 약자이다. 보면 알겠지만 보통 java에서 package를 정의할 때 쓰는 방식이고 이 팩키지를 jar로 묶어서 cos.jar라고 배포를 하는 것이다. cos.jar의 페이지에서 cos-05Nov2002.zip를 다운로드 가능하다.(파일명에서 보아 알겠지만 2002년 11월이 가장 최신판이다 ㅡ..ㅡ) 다운받은 파일안에 lib에 있는 cos.jar를 WAS쪽에 넣어도 되고 해당 프로젝트의 WEB-INF안에 lib안에 넣어도 된다. cos.jar를 사용한다고 했지만 정확히는 cos.jar의 MultipartRequest를 이용해서 파일을 받는다.
<%@ page import="com.oreilly.servlet.MultipartRequest" %>
<%
int maxPostSize = 10 * 1024 * 1024; // 10MB
saveDirectory = config.getServletContext().getRealPath("/upload");
MultipartRequest multi = new MultipartRequest(request, saveDirectory, maxPostSize, "utf-8");
Enumeration formNames=multi.getFileNames(); // 폼의 이름 반환
String fileInput = "";
String fileName = "";
String type = "";
File fileObj = null;
String originFileName = "";
String fileExtend = "";
String fileSize = "";
while(formNames.hasMoreElements()) {
fileInput = (String)formNames.nextElement(); // 파일인풋 이름
fileName = multi.getFilesystemName(fileInput); // 파일명
if (fileName != null) {
type = multi.getContentType(fileInput); //콘텐트타입
fileObj = multi.getFile(fileInput); //파일객체
originFileName = multi.getOriginalFileName(fileInput); //초기 파일명
fileExtend = fileName.substring(fileName.lastIndexOf(".")+1); //파일 확장자
fileSize = String.valueOf(fileObj.length()); // 파일크기
}
}
%>
위의 소스가 MultipartRequest를 이용한 파일 받기 예제이다. 소스가 그렇게 어렵지 않기 때문에 크게 어려운 부분은 없다고 생각한다. MultipartRequest에 파라미터를 넘기기 위해서 최대용량과 저장폴더의 실제경로를 미리 만들어 주고 MultipartRequest의 객체 생성을 할 때 request와 함께 넘겨준다. MultipartRequest는 아주 다양하게 오버라이딩되어 있기 때문에 전달해야하는 파라미터에 대해서는 원하는 대로 사용할 수 있다. MultipartRequest의 정의는 cos.jar쪽 페이지에 자세하기 나와 있다.
파일이 몇개가 넘어오든 간에 MultipartRequest에서는5번라인의 객체생성 부분에서 모두 파일이 업로드 되어 저장되고 그 뒤에 저장한 파일들의 정보를 가져오는 형태로 되어 있다. 이 말은 정보를 얻기 전에 파일 저장이 먼저 되기 때문에 파일의 어떤 정보를 검사해서 저장할지 말지를 결정하는 것이 안된다는 얘기고 저장한 다음에 검사를 해서 삭제해 주는 형태가 되어야 할 것이다.
7번 라인에서는 Enumeration으로 multi객체로 부터 전달받은 폼의 이름을 구해온다. 9~14번에서 while루프 안에서 사용한 변수들을 초기화 하고 16번라인부터 7번에서 구해온 폼이름을 통해서 파일객체를 가져오면서 다 가져올때까지 루프를 돈다. while형태로 되어 있기 때문에 단일 파일이든 여러개의 파일이 올라오든 모두 처리가 가능하다. while문 안에서는 각 파일의 정보를 구해온다. 이렇게 구해온 정보는 보통은 Database에 넣을 목적으로 구해 올 것이며 필요한 것만 가져와서 사용하면 되겠다.
여기서 input=file외에 다른 input에 대한 값들을 가져오려면 request.getParameter()대신에 multi.getParameter()를 사용해주면 된다. (여기서 multi는 위에 예제소스에서 만든 MultipartRequest의 객체이다.) 사용법은 동일하다.
각자 다르겠지만 일반적으로 서버에 파일을 저장할 때는 사용자가 올린 파일명을 그대로 올리지 않는다. 이는 여러가지 이유가 있는데 jsp같은 경우는 올리지 못하게 하는게 일반적이고 올리더라도 실행되지 않도록 확장자를 날리는 등의 조치를 취함으로써 보안처리를 해준다. 또한 같은 파일명이 있을 경우에 이전파일을 덮어쓰면 안되기 때문에 같은 파일명이 있을 경우에 대한 처리를 해야하고 우리같은 경우는 한글로 된 파일명은 서버에 따라서 문제가 생길수도 읽고 띄어쓰기나 특수기호등 파일명에 대한 안정성을 보장할 수 없기 때문에 나같은 경우는 대게 영문이나 숫자의 조합으로 특정파일명을 만들어서 저장한 뒤에 디비에는 서버쪽 파일명과 초기파일명을 둘다 넣어좋고 서버쪽 파일명을 이용해서 찾고 다운할때나 보여줄때는 초기파일명을 가지고 처리한다.
이렇게하려면 업로드할때 파일명을 바꾸어 주어야 한다. MultipartRequest객체를 생성할 때 파일명을 수정하도록 파라미터를 하나 더 주면 된다.
MultipartRequest multi = new MultipartRequest(request, saveDirectory, maxPostSize, "utf-8", new DefaultFileRenamePolicy());
4번째 파라미터로 DefaultFileRenamePolicy를 넘겨준다. DefaultFileRenamePolicy는 cos.jar는 안에 존재하는 클래스이다. 파일명을 어떻게 바꾼다라는 규칙이 정해져 있는 클래스를 파라미터로 넘겨주고 파일을 업로드 할때 그 규칙에 따라 파일명이 바뀌어서 올라간다. 여기서 DefaultFileRenamePolicy는 같은 파일명이 있는지를 검사하고 있을 경우에는 파일명뒤에 숫자를 붙혀준다. ex: aaa.zip, aaa1.zip, aaa2.zip 이런 식으로...
이 규칙에 따르면 중복에 대한처리는 할 수 있지만 그 외의 경우에는 대응할 수가 없다. 그래서 DefaultFileRenamePolicy의 규칙을 필요하다면 수정해 주어야 한다. cos.jar안에 src파일도 있으니 직접 수정하고 다시 jar로 만들어도 되고 cos.jar는 그대로 두고 따로 클래스를 만들어서 MultipartRequest객체를 생성할 때 내가 만든 클랙스를 넘겨주어도 된다.
MultipartRequest multi = new MultipartRequest(request, saveDirectory, maxPostSize, "utf-8", new MyFileRenamePolicy());
MyFileRenamePolicy 클래스는 FileRenamePolicy로 부터 상속도 받아야 하고 혹시모를 다른 충돌도 막기 위해서 DefaultFileRenamePolicy와 동일하게 com.oreilly.servlet.multipart라는 팩키지를 만들어서 넣는다.
DefaultFileRenamePolicy.java
package com.oreilly.servlet.multipart;
import java.io.*;
public class DefaultFileRenamePolicy implements FileRenamePolicy {
public File rename(File f) {
if (createNewFile(f)) {
return f;
}
String name = f.getName();
String body = null;
String ext = null;
int dot = name.lastIndexOf(".");
if (dot != -1) {
body = name.substring(0, dot);
ext = name.substring(dot); // includes "."
}
else {
body = name;
ext = "";
}
int count = 0;
while (!createNewFile(f) && count < 9999) {
count++;
String newName = body + count + ext;
f = new File(f.getParent(), newName);
}
return f;
}
private boolean createNewFile(File f) {
try {
return f.createNewFile();
}
catch (IOException ignored) {
return false;
}
}
}
이게 기존에 있는 DefaultFileRenamePolicy.java의 소스이다. 소스는 처음에는 좀 어색하지만 그리 어렵지 않다. rename()이 처음 호출되는데 createNewFile()를 실행해서(5라인) 파일생성이 가능하면 그대로 끝을 내고 생성을 못했으면 파일명의 점(.)을 기준으로 파일명과 확장자를 나누어서 저장한다.(13라인) 그리고 파일생성이 가능할 때까지(중복파일이 없을때까지) 루프를 돌아서 파일명+숫자+확장자형태로 다시 만들어서 리턴한다.(23라인)
MyFileRenamePolicy.java
package com.oreilly.servlet.multipart;
import java.io.*;
import java.util.Date;
import java.text.SimpleDateFormat;
public class AlmapFileRenamePolicy implements FileRenamePolicy {
public File rename(File f) {
long currentTime = System.currentTimeMillis();
SimpleDateFormat simDf = new SimpleDateFormat("yyyyMMddHHmmss");
int randomNumber = (int)(Math.random()*100000);
String uniqueFileName = "" + randomNumber + simDf.format(new Date(currentTime));
String name = f.getName();
String body = null;
String ext = null;
int dot = name.lastIndexOf(".");
if (dot != -1) {
body = name.substring(0, dot);
ext = name.substring(dot); // includes "."
}
else {
body = name;
ext = "";
}
String tempName = uniqueFileName + ext;
f = new File(f.getParent(), tempName);
if (createNewFile(f)) {
return f;
}
int count = 0;
while (!createNewFile(f) && count < 9999) {
count++;
String newName = uniqueFileName + "_" + count + ext;
f = new File(f.getParent(), newName);
}
return f;
}
private boolean createNewFile(File f) {
try {
return f.createNewFile();
}
catch (IOException ignored) {
return false;
}
}
}
새로 만든 파일 명명규칙인 MyFileRenamePolicy이다. 기존에 있던 것에서 파일명명하는 방식만 약간 바꾼 것이다. 앞에것보다는 약간 길어졌지만 간단하다. 파일중복을 줄이기 위해서 5자리짜리 랜덤수와 현재날짜시간의 조합으로 유니크한파일명을 만든다(12라인) 앞에것과 동일하게 점(.)을 기준으로 파일명과 확장자를 분리하고(18라인) 분리한파일명대신 유니크한 파일명과 확장자를 조합해서 임시로 파일명을 만든 다음에 파일생성을 시도한다.(28라인) 성공하면 그대로 끝내고 실패하면 루프를 돌면서 파일생성가능할때까지 카운트를 올려서 유니크한 파일명_카운트.확장자형태가 되도록 만든다.
이렇게 모든 파일은 랜덤수+현재시간.확장자의 형태로 만든다.여기서는 파일명으로 수자만 사용했지만 원하는 규칙대로 만들어서 클래스를 구성해 주면 그에 맞게 리네임을 할 수 있다.
좋은 자료 제 블로그에 감사히 담아가겠습니다 ^-^
예.. 감사합니다.
안녕하세요..
cos.jar 관련 검색을 하다가 발견하여 들어왔습니다.
저도 지금 was 서버를 사용하고 있는데요;;
패키지를 다운받아서 사용하는 것은 처음이라 문의를 드립니다;;
cos.jar를 was 서버의 어느부분에 다운받아야 하나요??
특정 폴더나 디렉토리가 있을꺼 같은데..
여기저기 찾아봐도 보이지가 않네요;;
jar팩키지는 WebContent/WEB-INF/lib안에 들어갑니다. 추가한다음에 WAS를 재시작하셔야 인식할 겁니다.
이클립스를 쓰시면 lib안에 넣으셔도 되고 properties의 java build path 의 libraries탭에서 add external jars로 cos.jar를 추가해 주시면 됩니다.
처음에는 항상 이런 기본적인게 잘 안나와있어서 힘들더군요... 잘 해결하시길...
현재 jDeveloper를 이용하여 개발하고 있습니다.
그럼 우선 WebContent/WEB-INF/lib 안에 넣어는 봤는데..
컴파일 해보니 인식이 안되더라구요..
아마 path 지정과 업데이트가 필요한것 같던데..
이리저리 해봤는데 잘 안되네요;;
여튼 좋은 답변 감사합니다..^^
jDeveloper는 제가 한번도 사용해보지 않아서 어떻게 말씀드릴수 없네요.
이클립스의 경우는 lib에 넣거나 외부jars추가해주면 Libraires로 볼 수 있거든요.
결론적으로 말씀드리자면 IDE에서 가상으로 돌리던 배포해서 WAS로 직접 돌리던 간에 운영되는 WAS의 입장에서 WebContent/WEB-INF/lib안에 jar가 존재하여야 합니다. jDeveloper가 어떤식으로 구동하는지 몰라서요. ^^;;
감사합니다.
새롭게 다시 시작하는데 좋은 정보 담아갈께요
방문해 주셔서 감사합니다. ^^
감사합니다.. 도움 많이 되었습니다. ^^
도움되셨다니 다행입니다. ^^;;
좋은 자료 담아갈께요
예~ CCL대로만 가져가시면 됩니다. 링크라도 알려주심 좋을텐데요.. ^^
좋은 자료 감사합니다.^^ 담아가겠습니다^^
예 감사합니다. CCL만 지켜주세요.
myFileRenamePolicy 를
cos.jar 안에 넣는방법좀 상세히 알려주실수있나요???
이클립스에서 cos.jar 다 풀어서 다시 압축해
질문하시다가 글이 약간 짤린것 같네요 ㅜ..ㅜ
jar파일에 대해서는 검색하시면 더 자세한 내용이 많이 나올것이고요.
압축푸시고 올바른 팩키지위치에 넣으시고 재패키징하시면 됩니다.
그냥 테스트 용도라면 jar로 만들지 않고 위에 적혀져 있는 이클립스에서 펙키지와 똑같이 만들어놓고 거기에 추가해서 돌려도 동작합니다.
감사합니다..
똑같은 패키지 만들어서
기존 jar 파일 압축 풀어서 패키지에 넣고
jar -cvf cos.jar . 하니 돼더군요
근데 이클립스에서 새로운 클래스는 자동으로 임포트가 안돼는데 그건 왜그런걸까요???
잘 해결되셨군요.. ㅎㅎㅎㅎㅎ
2번째 질문은 잘 모르겠네요 ㅠ..ㅠ
새로 만든 클래스들이 그런건가요? 보통은 잘 되는데요 아직 경험해보질 못해서요...
친절한 설명과 좋은 자료 감사합니다.
도움이 많이 되었어요~^^
도움되셨다니 다행이네요. ^^
댓글 감사합니다... 즐코딩 되세요~ ㅎ
좋은 자료 감사합니다. 많이 도움이 되네요^^
예~ 댓글 감사합니다. ㅎ
form 의 file 태그의 이름을 다동일하게 주면 마지막파일만 올라오는 버그가 있던데 그부분은 해결이 된건가여?? 개발하신분이 아니라 여기에..올릴게 아닌가?
저도 현재는 cos.jar를 안쓰고 있어서 버그에 대해서는 잘 모르겠습니다.
근데 form submit의 특성상 name이 같으면 하나로 처리되는게 맞는 동작 아닌가요?
와 설명 완전 잘 해놓으셨네요 !!!!!!!!!
좋은 글 잘 보고 갑니다^^
감사합니다. ㅎㅎ
우와 정말 설명보고 감동이 물결이...
잘보고 담아갑니다
아직 이 예제가 돌아간다는거에 종종 놀라기도 합니다만 도움되셨다니 다행이네요..
너무좋은 자료네요 퍼가도 될까요
오래된 자료인데 아직 유효한가보군요. ㅎㅎㅎ CCL만 따라서 퍼가셔도 됩니다.
공부하다가 찾아들어왔는데 많은 도움이 되었습니다. 감사합니다.
DefaultFileRenamePolicy 오버라이드하는 class 만들고 rename 오버라이딩 해준다음 그 class로 인자값 넣어주면 되지않나요?
너무 오래전 글이라 이젠 제가 동작을 확인하기가 어렵네요 ㅠ