sitelink1  
sitelink2  
sitelink3 http://1 
sitelink4 http://ko 
sitelink5 http://www.javanuri.net/devforum/boardVi...amp;gb=qna 
sitelink6 http://sitelink1 
multipart form parser - http file upload 기능 java class 연재1 

다음과 같은 기능의 file upload 프로그램을 java code를 소개하겠습니다. 

1. 대용량 파일 업로그 기능 (1G 이상 파일 업로드에도 문제가 없는 기능) 
2. 파일 업로드시 upload 진행 상황을 Internet Explorer에서 볼수 있을것 

위의 기능 중에서 이번 글에서는 1번 기능에 대해서 설명을 하겠습니다. 
2번 기능은 AJAX 기능을 이요하여 구현이 가능하며 이후 글에서 차차 설명하겠습니다. 
첨부하는 소스코드는 실제 프로젝트에서 사용하는데 무리가 없는 소스입니다. 
(많은 시스템에서 실제 운영중인 코드 입니다.) 

ActiveX를 제외한 일반적인 파일 업로드 프로그램은 클라이언트에서 서버로 전달된 
multipart form 형식의 input stream을 parsing하여 어떤 값이 파일이고 어떤 값이 
form의 input 값인지를 결정하여 파일은 file system에 저장하는 방식입니다. 

다음과 같은 html form을 submit하겠습니다. 
선택한 파일은 c:test.txt 파일입니다. 

<form name="fParam" encType="multipart/form-data"> 
<input type="hidden" name="_cmd" value="file_data"> 
<input type="file" name="file"> 
</form> 

서버에 전달되는 stream은 다음과 같습니다. 

-----------------------------7d82efc203cc 
Content-Disposition: form-data; name="_cmd" 

uploadFile 
-----------------------------7d82efc203cc 
Content-Disposition: form-data; name="file"; filename="C:test.txt" 
Content-Type: text/plain 

test 
-----------------------------7d82efc203cc-- 

이와 같은 stream을 parsing하는 java class인 
WCMultipartFormParser,WCMultipartFormElement를 소개합니다. 
위와 같은 stream을 parsing하면 form element가 2개이고 하나는  
parameter(_cmd)이고 하나는 파일(c:test.txt)입니다. 

WCMultipartFormParser 
  -> WCMultipartFormElement : _cmd=uploadFile 
  -> WCMultipartFormElement : file c:test.txt 

WCMultipartFormParser(parser라고 간략히 부르겠습니다.) 
WCMultipartFormElement(element라고 간략히 부르겠습니다.) 
의 구조를 설명하겠습니다. 

대용량 파일을 서버에 업로드 하려면 input stream으로 부터 전달된 file의 data를 
memory에 모두 loading하면 안됩니다. 만약 5G file을 uploading한다면 이를 memory에 올릴 수가  
없기때문입니다. 따라서 upload된 내용을 parsing하면서 임시 directory에 저장을 합니다. 

다음과 같은 순서로 설명을 하겠습니다. 

1. input stream을 buffering하는 방법 
2. parser에서 호출되는 순서 
3. file을 임시파일로 저장(다음번 글에서 설명) 
4. 임시로 저장된 파일을 사용자가 정의하는 directory로 복사(다음번 글에서 설명) 

parser는 input stream으로부터 읽어들인 내용을 double buffering하여 관리합니다. 
double buffering에 대해서 간단히 설명드리겠습니다. 
하나의 buffer를 반으로 나누고 한쪽 부분에 input stream에서 읽은 내용을 임시로 저장합니다. 
그리고 이 내용을 parsing을 합니다. 
한쪽 부분 buffer를 모두 parsing을 하면 buffer의 나머지 반쪽에 다시 input stream에서 읽은 
내용을 임시로 저장합니다. 
이렇게 double buffering을 하는 이유는 한쪽의 buffer를 parsing하다가 parsing이 잘못되어 
rollback을 하는 경우 한쪽 buffer에서만 rollback해서 안되는 경우가 있습니다. 
예를 들어 보이겠습니다. 
buffer size = 20이고 반으로 나눈 한쪽 버퍼는 10 size가 됩니다. 
1234567890 1234567890 
abcdefghij kl 
아래와 같이 kl까지 parsing을 했는제 잘못 parsing을 해서 rollback 즉 gh부터 parsing을 다시 
해야 하는 경우 double buffering 기법을 사용합니다. 
그러면 buffer size를 크게 하면 되지않느냐 라고 질문 할 수 있습니다. 그렇지 않습니다. 
buffer size가 아무리 커도 rollback에 대한 여분을 충분히 확보할 수가 없습니다. 
즉 이전에 사용하던 buffer를 지우지 말고 보유하고 있어야 충분한 rollback을 확보할 수 있습니다. 
첨부한 source 코드에서 getByte는 하나의 input stream에서 1 byte 씩 읽어들입니다. 
getByte에서 호출하는 getByteEx에서 double buffering 처리를 합니다. 

public byte getByte() throws Exception 

input stream을 parsing 하는 부분에서는 이제 byte단위로 parsing하다가 문제가 있으면 
rollback을 할 수 있습니다. 
위에서 sample로 보여드린 form 즉 _cmd,file을 parsing하는 방법에 대해 설명하겠습니다. 

WCMultipartFormParser.init() 
  -> parseContent(oRequest) 
    -> parseContentEx(oRequest) 
      -> findBoundary() : -----------------------------7d82efc203cc 이 부분을 parsing합니다. 
      -> findHeader() : Content-Disposition: form-data; name="_cmd" 이 부분을 parsing 합니다. 
        -> WCMultipartFormElement.setHeader : header 정보를 element에 저장합니다. 
      -> procContent() : content body를 parsing 합니다. 
        -> 임시파일에 파일 저장 : content type이 file인경우 
        -> parameter인경우 WCProperties m_rParam 에 parameter 이름과 value를 저장 

위와 같은 구조로 form을 parsing합니다. 

<form name="fParam" encType="multipart/form-data"> 
<input type="hidden" name="_cmd" value="file_data"> 
<input type="file" name="file"> 
</form> 

이 form을 submit할때 request parameter 값은 WCMultipartFormParser.getParam()을 호출하면 
parsing되어 저장된 request parameter값을 얻어올 수 있습니다. 
그리고 아래의 소스에서 WCMultipartFormParser.saveFile(sDir)을 호출하여 임시 저장된 
file들을 개발자가 원하는 directory로 이동을 합니다. 

다음번 글에서는 file을 저장하는 구조에 대해서 설명하겠습니다. 

아래의 소스 코드는 JSP 에서 WCPage의 object를 생성하고 WCMultipartFormParser의 기능을 호출하여 
multipart form을  parsing하고 파일을 저장하는 sample code입니다. 

WCPage.initCtrl 
  -> WCPage.initCtrl 
    -> WCPage.isMultipart : file을 submit한 겨우인지를 검사 
      -> WCMultipartFormParser.init() 

if (isMultipart()) 

    WCMultipartFormParser oFormParser = new WCMultipartFormParser(this); 
    oFormParser.setDebug(true); 
    oFormParser.init(); 
    byte[] btDebugBuf = oFormParser.getDebugBuf(); 
    String sDebug = new String(btDebugBuf); 
    this.printOut("<pre>"); 
    this.printOut(sDebug); 
    this.printOut("</pre>"); 
     
    WCProperties rParam = oFormParser.getParam(); 
    this.printOut(rParam.serializeOut()); 

    String sDir = "c:/tmp3"; 
    oFormParser.saveFile(sDir); 
    return 1; 

       
여기서 잠깐 WCPage에 대해서 설명하겠습니다. 
WCPage는 JSP 혹은 Servlet에 대한 정보를 저장하고 있는 Object입니다. 
WCMultipartFormParser 혹은 WCMultipartFormElement에서 request 혹은 response object를 access할때 
WCPage를 통하여 access할 수 있습니다. 
WCPage.initCtrl에서 pageContext를 넘김으로 WCPage에서 JSP 관련 context를 모두 저장하고 
또한 page로 전달된 parameter를 WCPage가 모두 저장하고 있습니다. 
여기서 sample로 제공하는 WCPage java class는 WDL(Web Development Library)에서 제공하는 WCPage.java에서 
필요한 부분만 추출하여 새로 구성한 sample입니다. 
(개발자들이 이해를 쉽게 하기 위해서 일부러 추출을 하였습니다. http://www.webdevlib.net 에서  
모든 소스코드는 무료로 다운로드 받을 수 있습니다.) 

첨부한 소스만으로도 어느정도 동작이 가능하지만 WCProperties,WCVector,WCLog,WCSystem,WCEnv 
등의 파일은 http://www.webdevlib.net에서 다운받아 사용하십시요 
소스의 일부분은 고의로 누락한 것은 아닙니다. 모든 소스를 설명하려니 너무 많기때문에 
중요한 부분을 추출하여 sample로 구성하면서 최대한 이해가 쉽도록 재구성을 하였습니다. 

첨부파일에는 3개의 파일이 zip으로 묶여있습니다. 

WCMultipartFormParser.java 
WCMultipartFormElement.java 
file_upload_progress1.jsp 

-- file_upload_progress1.jsp 소스 시작 
<%@ page language="java" import="wdl.*,java.util.*,java.sql.*,java.lang.*,java.io.*,java.io.File " contentType="text/html; charset=EUC-KR"%> 
<% 
    WCPage oPage = new WCPage(); 
    if (oPage.initCtrl(pageContext) > 0) 
    { 
        return; 
    } 
%> 
<%@ include file="/wdl/src/java/WCMultipartFormParser.java"%> 
<%@ include file="/wdl/src/java/WCMultipartFormElement.java"%> 
<%! 
public class WCPage 

    public int initCtrl(javax.servlet.jsp.PageContext oPageContext) 
    { 
        return initCtrl( 
            (javax.servlet.http.HttpServletRequest)oPageContext.getRequest() 
            ,(javax.servlet.http.HttpServletResponse)oPageContext.getResponse() 
            ,(javax.servlet.jsp.JspWriter)oPageContext.getOut() 
            ,(javax.servlet.http.HttpSession)oPageContext.getSession() 
            ,(javax.servlet.ServletContext)oPageContext.getServletContext() 
            ,oPageContext 
            ,(javax.servlet.ServletConfig)oPageContext.getServletConfig() 
            ); 
    } 
    public int initCtrl( 
        javax.servlet.http.HttpServletRequest oRequest 
        ,javax.servlet.http.HttpServletResponse oResponse 
        ,javax.servlet.jsp.JspWriter oOut 
        ,javax.servlet.http.HttpSession oSession 
        ,javax.servlet.ServletContext oApplication 
        ,javax.servlet.jsp.PageContext oPageContext 
        ,javax.servlet.ServletConfig oConfig) 
    { 
        try 
        { 
            m_request = oRequest; 
            m_response = oResponse; 
            m_out = oOut; 
            m_session = oSession; 
            m_application = oApplication; 
            m_pageContext = oPageContext; 
            m_config = oConfig; 
             
            if (isMultipart()) 
            { 
                WCMultipartFormParser oFormParser = new WCMultipartFormParser(this); 
                oFormParser.setDebug(true); 
                oFormParser.init(); 
                byte[] btDebugBuf = oFormParser.getDebugBuf(); 
                String sDebug = new String(btDebugBuf); 
                this.printOut("<pre>"); 
                this.printOut(sDebug); 
                this.printOut("</pre>"); 
                 
                WCProperties rParam = oFormParser.getParam(); 
                this.printOut(rParam.serializeOut()); 

                String sDir = "c:/tmp3"; 
                oFormParser.saveFile(sDir); 
                return 1; 
            } 
        } 
        catch (Exception ex) 
        { 
        } 
        return 0; 
    } 
    public javax.servlet.http.HttpServletRequest m_request = null;  
    public javax.servlet.http.HttpServletResponse m_response = null;  
    public javax.servlet.jsp.JspWriter m_out = null;  
    public javax.servlet.http.HttpSession m_session = null;  
    public javax.servlet.ServletContext m_application = null;  
    public javax.servlet.jsp.PageContext m_pageContext = null;  
    public javax.servlet.ServletConfig m_config = null;  
     
    public javax.servlet.http.HttpSession getSession() 
    { 
        return m_session; 
    } 
    public javax.servlet.http.HttpServletRequest getRequest() 
    { 
        return m_request; 
    } 
    public javax.servlet.jsp.PageContext getPageContext() 
    { 
        return m_pageContext; 
    } 

    public boolean isMultipart() 
    { 
        HttpServletRequest oRequest = (javax.servlet.http.HttpServletRequest)m_request; 
        String content_type = oRequest.getHeader("content-type"); 
        if (WCString.indexOf(WCString.toUpper(content_type),"MULTIPART") == 0) 
        { 
            return true; 
        } 
        return false; 
    } 

%> 

<form name="fParam" encType="multipart/form-data"> 
<input type="hidden" name="_cmd" value="file_data"> 
<input type="file" name="file"> 
</form> 

<a href="javascript:uploadFile();">upload the file</a> 

<iframe id="ifrmAction" name="ifrmAction" width="0" height="0"></iframe>  

<script> 
function uploadFile() 

    var fParam = window.document.fParam; 
    fParam._cmd.value = "uploadFile"; 
    fParam.method = "post"; 
    fParam.target = "_blank"; 
    fParam.submit(); 

</script> 
-- file_upload_progress1.jsp 소스 끝 

출처 : 고급 웹 UI 개발 라이브러리 Web Development Library 소스공개 : http://www.webdevlib.net 

첨부파일 : multipart_form_parser1.zip
번호 제목 글쓴이 날짜 조회 수
171 메모리 유출과 약한 참조 황제낙엽 2010.01.26 616
170 Methods of the Matcher Class 황제낙엽 2010.01.19 120
169 Pattern.matches() , Matcher.matches() , Matcher.find() file 황제낙엽 2010.01.19 105
168 java.lang.IllegalArgumentException 황제낙엽 2010.01.18 130509
167 org.apache.commons.fileupload.servlet.ServletFileUpload 를 이용한 파일 업로드 file 황제낙엽 2009.11.19 129
166 Error reading tld listeners java.lang.NullPointerException 황제낙엽 2009.10.14 67
165 Cannot find the tag library descriptor for “http://java.sun.com/jsp/jstl/core 황제낙엽 2009.10.14 1006
164 Transfer-Encoding: chunked VS Content-Length 황제낙엽 2009.09.17 154
163 서블릿 응답 헤더(Response Header) 황제낙엽 2009.09.17 80
162 같은 문자열인데도 정규식에서 해당 문자열을 파싱하지 못하는 경우 황제낙엽 2009.08.08 39
161 MultipartRequest (cos.jar)와 서블릿을 이용한 업로드 file 황제낙엽 2009.06.19 384
160 [대용량 파일 업로드] multipart form parser - http file upload, database 저장 java class 연재2 file 황제낙엽 2009.06.19 1831
» [대용량 파일 업로드] multipart form parser - http file upload 기능 java class 연재1 file 황제낙엽 2009.06.19 1436
158 [reflection/리플렉션] Class.forName 황제낙엽 2009.05.27 101
157 문자열 내의 공백을 제거하는 간단한 정규식 황제낙엽 2009.05.20 88
156 문자열에서 특수 문자 (Escape Sequence) 처리 file 황제낙엽 2009.02.20 1322
155 정규표현식을 사용하는 String클래스의 replaceAll() 함수 개량 황제낙엽 2009.02.09 219
154 File 복사 함수 황제낙엽 2009.02.08 31
153 JSP session 정보 얻기 황제낙엽 2009.01.21 127
152 서버상의 로컬경로 (실제경로) 관련 환경변수 황제낙엽 2009.01.21 339