목차

엑셀 처리 가이드

엑셀 연동 방식 개요

엑셀 연동 방식에 대한 개요 및 구조는 아래와 같습니다. 연동 방식에 대한 변경은 실행 파라미터에 의해서 결정됩니다.

엑셀 연동 방식 개요

구분 WAS 서버 연동 방식 로컬 엑셀 오토메이션 방식 로컬 스크립트 방식
기반 기술
  • xFrame5 EXCEL 처리 스크립트 모듈
  • Apache POI 라이브러리
  • xFrame5 XEXCEL 자바 라이브러리
  • xFrame5 EXCEL 처리 스크립트 모듈
  • 엑셀 오토메이션 기술
  • xFrame5 전용 브라우저
  • xFrame5 EXCEL 처리 스크립트 모듈
  • 엑셀 처리용 오픈소스 자바스크립트 라이브러리
xFrame5
실행 환경
  • 범용 브라우저, xFrame5 전용 브라우저
  • xFrame5 전용 브라우저 (범용 브라우저 불가)
  • 범용 브라우저, xFrame5 전용 브라우저
실행 환경
  • Java 기반 WAS 서버
  • JDK 1.6 이상
  • 고객사에 적용된 DRM 솔루션 회사에서 제공하는 DRM 해제/설정 자바 라이브러리
  • 사용자 PC에 엑셀 프로그램이 설치되어 있어야 함
  • 실행 환경제약 사항 없음
  • 기본 방식으로는 DRM 해제/설정 불가
처리 속도
  • 보통
  • 조금 우수
    (엑셀 오토메이션 작업으로 인한 처리 시간 증대)
  • 우수
서버 부하
  • 대량 데이터 및 사용자가 많은 경우, 서버에 부하(메모리, CPU) 발생
  • 서버 부하 없음
  • 서버 부하 없음
파일 선택 및
저장 경로
  • 사용자 선택 (직접 경로 지정 불가)
  • 사용자 선택 및 직접 경로 지정 가능
  • 사용자 선택 (직접 경로 지정 불가)

DRM 처리

DRM 처리 방식 상세 내용

엑셀 연동 방식에 대한 개요 및 구조는 아래와 같습니다. 연동 방식에 대한 변경은 실행 파라미터에 의해서 결정됩니다.

구분 WAS 서버 연동 방식 로컬 엑셀 오토메이션 방식 로컬 스크립트 방식
엑셀 파일
업로드
DRM 해제
  • DRM 솔루션 회사에서 제공하는 DRM 해제 자바 라이브러리를 연동하여 xFrame5 XEXCEL 자바 라이브러리에서 DRM 해제 수행
  • 엑셀 프로그램에서 DRM 해제

기본적으로 DRM 해제 불가로 인해 엑셀 업로드 동작 오류 발생
[DRM 해제 방안]
- 방안1 (권고 방안)
xFrame5 전용 브라우저인 경우, DRM 솔루션에서 xFrame5 전용 브라우저를 엑셀 프로그램처럼 DRM 자동 해제 방식 적용 가능
- 방안 2
xFrame5 전용 브라우저인 경우, 엑셀 업로드시 DRM 해제를 위한 DRM 솔루션 연계 커스트마이징 개발 필요
- 방안 3
GRID_EXCELUPLOAD_METHOD2_DRMURL 실행 파라미터를 지정하여, 엑셀 업로드시 DRM 솔루션 연계한 DRM 해제 처리 모듈(하단 XDrmUtil.java 소스 참조) 커스트마이징 개발

엑셀 파일
다운로드
DRM 적용
  • DRM 솔루션 회사에서 제공하는 DRM 적용 자바 라이브러리를 연동하여 xFrame5 XEXCEL 자바 라이브러리에서 수행
  • 엑셀 저장시 DRM 적용이 필요한 경우, “로컬 스크립트 방식”으로 엑셀 파일 저장 후
  • 엑셀 프로그램 기동 및 저장 작업을 수행하여 DRM 적용

기본적으로 엑셀 파일 저장시 DRM 적용 안됨
[DRM 적용 방안]
- 방안 1 (권고 방안)
DRM 솔루션에서 자동으로 DRM 적용 처리
- 방안 2
xFrame5 전용 브라우저인 경우, 엑셀 저장시 DRM 적용을 위한 DRM 솔루션 연계 커스트마이징 개발 필요
- 방안 3
GRID_EXCELDOWNLOAD_METHOD2_DRMURL 실행 파라미터를 지정하여, 엑셀 저장시 DRM 솔루션 연계한 DRM 적용 처리 모듈(하단 XDrmUtil.java 소스 참조) 커스트마이징 개발

고객사
보안 솔루션
정책 확인
  • 범용 브라우저/xFrame5 전용 브라우저에서 WAS 서버로 파일 업로드 보안 허용
  • 범용/xFrame5 전용 브라우저에서 파일 다운로드 보안 허용
xFrame5 전용 브라우저에서 파일 다운로드 보안 허용 범용 브라우저/xFrame5 전용 브라우저에서 파일 다운로드 보안 허용

엑셀 업로드 상세 방식

엑셀 업로드 상세 방식

엑셀 업로드 방식에 대한 자세한 내용은 아래와 같습니다.

구분 WAS 서버 연동 방식 로컬 엑셀 오토메이션 방식 로컬 스크립트 방식
엑셀 업로드 절차
  • 사용자가 엑셀 파일 선택 (API에서 직접 경로 지정 불가)
  • 사용자가 선택한 파일을 WAS 서버에 HTTP 멀티 파트 형태로 업로드
  • WAS 서버의 xFrame5 XEXCEL 자바 라이브러리에서 업로드된 엑셀 파일 파싱 및 데이터 추출하여 리턴
  • GRID에서 수신된 데이터 처리 및 표시
  • 사용자가 엑셀 파일 선택 (API에서 직접 경로 지정 가능)
  • xFrame5 전용 브라우저에서 엑셀 프로그램을 기동하여 엑셀 프로그램에서 엑셀 파일 로드 후, 엑셀 프로그램과의 인터페이스를 통해 데이터 추출 및 리턴
  • GRID에서 추출된 데이터 처리 및 표시
  • 사용자가 엑셀 파일 선택 (API에서 직접 경로 지정 불가)
  • xFrame5 XEXCEL 스크립트 라이브러리에서 사용자가 선택한 엑셀 파일 파싱 데이터 추출 및 리턴
  • GRID에서 추출된 데이터 처리 및 표시
실행 파라미터 GRID_EXCELUPLOAD_METHODTYPE 파라미터
  • 엑셀 다운로드 방식 지정
  • 파라미터 값 : 0 (기본값)
GRID_EXCELUPLOAD_METHODTYPE 파라미터
  • 엑셀 업로드 방식 지정
  • 파라미터 값 : 1
GRID_EXCELUPLOAD_METHODTYPE 파라미터
  • 엑셀 업로드 방식 지정
  • 파라미터 값 : 2

엑셀 다운로드 상세 방식

엑셀 다운로드 상세 방식

엑셀 다운로드 방식에 대한 자세한 내용은 아래와 같습니다.

구분 WAS 서버 연동 방식 로컬 엑셀 오토메이션 방식 로컬 스크립트 방식
엑셀 다운로드
절차
  • 그리드의 내용을 WAS 서버에 HTTP POST 형태로 송신
  • WAS 서버의 xFrame5 XEXCEL 자바 라이브러리에서 수신된 데이터를 이용하여 엑셀 파일 생성 및 엑셀 파일 리턴
  • 수신된 엑셀 파일 저장
  • 경로 지정 파일 저장 불가 (저장시 사용자가 폴더 지정 경로 지정)
  • 그리드의 내용을 xFrame5 XEXCEL 스크립트 라이브러리에서 엑셀 데이터 생성하여 xFrame5 전용 브라우저에게 전달
  • xFrame5 전용 브라우저에서 엑셀 프로그램 기동 및 저장
  • 경로 지정 파일 저장 가능
  • XLSX 파일 형식만 지원
  • 그리드의 내용을 xFrame5 XEXCEL 스크립트 라이브러리에서 엑셀 파일 생성 및 엑셀 파일 저장
  • 경로 지정 파일 저장 불가 (파일 저장시 사용자가 폴더 지정 경로 지정)
실행 파라미터 GRID_EXCELDOWNLOAD_METHODTYPE 파라미터
  • 엑셀 다운로드 방식 지정
  • 파라미터 값 : 0 (기본값)
GRID_EXCELDOWNLOAD_METHODTYPE 파라미터
  • 엑셀 다운로드 방식 지정
  • 파라미터 값 : 1
GRID_EXCELDOWNLOAD_METHODTYPE 파라미터
  • 엑셀 다운로드 방식 지정
  • 파라미터 값 : 2
세부 옵션
실행 파라미터
XEXCEL_DOWNLOAD_URL 파라미터
  • XExcelDownload 서블릿 URL 지정
  • 예시 - ”./xframe5/XExcelDownload”
엑셀 파일
비밀번호 설정
  • XExcelUpload 서블릿에서 엑셀 파일 비밀번호 지정 처리
  • 로컬 스크립트에서 엑셀 파일 비밀번호 지정 처리
  • 처리 시간이 조금 더 걸림

제약 사항

상세 내용

엑셀 연동 관련 제약 사항에 대한 자세한 내용은 아래와 같습니다.

구분 WAS 서버 연동 방식 로컬 엑셀 오토메이션 방식 로컬 스크립트 방식
엑셀 업로드
파일크기
  • 그리드 excel_upload_maxsize 속성
  • 그리드 get/setuploadexcelmaxsize API 제공
  • N/A
  • N/A
실행 파라미터 GRID_EXCELUPLOAD_MAXSIZE 파라미터
  • 업로드할 파일 최대 크기 지정
  • 기본값: 0 (제한없음)
  • 단위: 바이트
  • 그리드 excel_upload_maxsize 속성이 0인 경우 사용됨

XDrmUtil.java 소스

XDrmUtil.java 파일은 GRID_EXCELUPLOAD_METHODTYPE/GRID_EXCELDOWNLOAD_METHODTYPE 실행 파라미터가 2인 경우, 각각 엑셀 파일에 대한 DRM 해제 및 적용 처리를 위한 샘플 자바 서블릿 소스이다.

프로젝트 진행시에는 고객사의 DRM 솔루션과의 연계 로직을 추가하여 사용해야 한다.

소스 파일

소스

package xframe5.template.html5.component.grid;

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

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.io.InputStream;
import java.io.PushbackInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.servlet.ServletContext;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.output.DeferredFileOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class XDrmUtil extends HttpServlet
{
	private static final long serialVersionUID = 3347918932345852889L;

	/****************************************************************
	 * 프로젝트 적용시 변경 대상 시작
	 ****************************************************************/
	protected final Log logger = LogFactory.getLog(getClass());

	// 업로드된 파일 처리용 임시 파일을 저장할 디렉토리 절대 경로 (프로젝트 적용시 변경필요)
	String	tempDirAbsolutePath = "C:\\xFrame5\\temp";

	// 업로드된 파일을 임시로 저장할 디렉토리 절대 경로 (프로젝트 적용시 변경필요)
	String	saveBaseDirAbsolutePath = "C:\\xFrame5\\temp";

	int		maxMemoryFileSize = 10;				// maximum memory file size
	int		maxFileSize =  1000 * 1024 * 1024;	// maximum file size (1000MB)

	/**
	 * 메시지 로깅
	 * @return
	 */
	public void logMsg(String msg)
	{
		// logger.info(msg);
		System.out.println(msg);
	}

	/**
	 * 파일 업로드 처리시 사요한 임시 저장 디렉토리 및 파일 저장 디렉토리 설정
	 * (getContextRootPath 함수 활용)
	 * @return
	 */
	public void setTempAndSaveDirPath(HttpServletRequest request)
	{
		String contextRootAbsolutePath;

		// Web Application이 실행되는 루트 절대 경로를 구함
		contextRootAbsolutePath = getContextRootPath(request);

		if (contextRootAbsolutePath.endsWith(File.separator) == false) {
			contextRootAbsolutePath += File.separator;
		}

		logMsg("contextRootPath = [" + contextRootAbsolutePath + "]");

		// 업로드된 파일 처리용 임시 파일을 저장할 디렉토리 절대 경로 (프로젝트 적용시 변경필요)
		// tempDirAbsolutePath = "C:\\xFrame5\\temp";
		tempDirAbsolutePath = contextRootAbsolutePath + "temp";
		logMsg("tempDirAbsolutePath = [" + tempDirAbsolutePath + "]");

		// 업로드된 파일을 임시로 저장할 디렉토리 절대 경로 (프로젝트 적용시 변경필요)
		// saveBaseDirAbsolutePath = "C:\\xFrame5\\temp";
		saveBaseDirAbsolutePath = contextRootAbsolutePath + "temp";
		logMsg("saveBaseDirAbsolutePath = [" + saveBaseDirAbsolutePath + "]");
	}

	/**
	 * Request에서 전달된 파라미터 KEY/VALUE 맵과 저장된 파일 경로를 이용하여
	 * DRM 적용/해제 및 DRM 적용/해제된 파일 절대 경로 반환
	 * @param paramMap Request에서 전달된 파라미터 KEY/VALUE 맵
	 * @param saveFileAbsolutePath 저장된 파일 경로
	 * @return  DRM 적용/해제된 파일 절대 경로 반환
	 */
	public String drmProcess(HashMap<String, String> paramMap, String saveFileAbsolutePath)
	{
		String returnFileAbsolutePath;

		// TODO: DRM 적용 또는 해제 처리 및 처리 결과 파일 경로 반환
		returnFileAbsolutePath = saveFileAbsolutePath;

		return returnFileAbsolutePath;
	}

	/****************************************************************
	 * 프로젝트 적용시 변경 대상 끝
	 ****************************************************************/
	String	errorMsg = "";		// error message
	String	fileName = "";		// uplaod file name
    String  browserType = "";	// browser type

	public void doGet(HttpServletRequest request, HttpServletResponse response) {
        doPost(request, response);
    }

	public void doPost(HttpServletRequest request, HttpServletResponse response) {
		ServletFileUpload 	uplaodHandler = null;			// file uplaod handler
		List<?>				fileItemList = null;			// file item list
		FileItem 			fileItem = null;				// file item
		String				saveFileAbsolutePath = "";

		byte[] 				buffer = null;
		int 				numRead = -1;

	    FileInputStream 		fis = null;
	    ServletOutputStream 	sos = null;

	    // POST 방식으로 전달되는 파일 이외의 값을 저장하기 위한 HashMap
		HashMap<String, String> paramMap = new HashMap<String, String>();

		// 임시로 파일 이름 및 절대 경로를 지정함
		// 실제 환경에서는 request에서 파일 이름, 저장할 파일 이름을 구하고, 파일 이름에 해당하는 절대 경로를 구해야 함.
		String 				returnFileAbsolutePath = "";

		logMsg("XDrmUtil Start ----------------------------------------------------");

		try {
			// Get Response Output Stream
	        sos = response.getOutputStream();

			setTempAndSaveDirPath(request);

	        // Set Response Common Header
	        setResponseCommonHeader(request, response);

			// Get Upload File Processor Object
			uplaodHandler = getFileUploadProcessor();
			logMsg("Success to create a file upload handler");

			// Parse request Data
			fileItemList = uplaodHandler.parseRequest(request);

			// Get Parameter Key/Value Map
			getParamValueMap(fileItemList, paramMap);

			// Process File Data
			Iterator<?> iter = fileItemList.iterator();
			while (iter.hasNext()) {
				fileItem = (FileItem)iter.next();

			    // Skip Normal Form Field Item
			    if (fileItem.isFormField()) { continue; }

		   		// Save Upload File Multipart Data
		   		saveFileAbsolutePath = handleUploadFile(fileItem, paramMap);
		   		if (saveFileAbsolutePath == null) {
		   			logMsg("saveFileAbsolutePath is null, errorMsg = " + errorMsg);
		   			returnErrorMsg(response, sos, errorMsg);
					return;
		   		}

		   		break;
			}

			// DRM 적용/해제 처리 및 처리 결과 파일 경로를 returnFileAbsolutePath 변수에 저장
			returnFileAbsolutePath = drmProcess(paramMap, saveFileAbsolutePath);

			// Create Byte Array For Read File
			buffer = new byte[4096];

			// Create File Object For Return File Path
	        File file = new File(returnFileAbsolutePath);
	        long fileSize = file.length();

	        // Set HTTP Response HTTP Header
	        setResponseHeader(response, fileName, fileSize);

	        // Read Return File Content & Write File Content to Output Stream
	        fis = new FileInputStream(new File(returnFileAbsolutePath));
	        while((numRead = fis.read(buffer, 0, 4096)) != -1){
	        	sos.write(buffer, 0, numRead);
	        }

	        sos.flush();
	    }
		catch (Exception e) {
	        logMsg(e.getMessage());
	        e.printStackTrace();

	        // Return Error Message
	    	try { returnErrorMsg(response, sos, e.getMessage()); }
	    	catch (Exception ignore) {}
	    }
		catch (Throwable e) {
	    	logMsg(e.getMessage());
			e.printStackTrace();
		}
		finally {
			// Close File Input Stream
	    	if (fis != null) {
	    		try { fis.close(); }
	    		catch (Exception ignore) {}
	    	}

	    	// Close Servlet Output Stream
	    	if (sos != null) {
	    		try { sos.close(); }
	    		catch (Exception ignore) {}
	    	}

			logMsg("delete " + saveFileAbsolutePath);
			deleteFile(saveFileAbsolutePath);
			logMsg("delete " + returnFileAbsolutePath);
			deleteFile(returnFileAbsolutePath);
		}
    }

	public void returnErrorMsg(HttpServletResponse response, ServletOutputStream sos, String errorMsg) throws IOException
	{
		// 에러 발생시 메세지 리턴용
		// 	StartDelimiter + 에러메시지 + EndDelimiter
		String	WFM_DATASTART_DEL	= String.valueOf((char)0x1C) + "xFrame5ErrorS" + String.valueOf((char)0x1C);	// data start indicator
		String	WFM_DATAEND_DEL		= String.valueOf((char)0x1F) + "xFrame5ErrorE" + String.valueOf((char)0x1F);	// data end indicator

		String errMsg = WFM_DATASTART_DEL + errorMsg + WFM_DATAEND_DEL;

		byte[] arrReturn = errMsg.getBytes("UTF-8");

		response.setContentType("text/html");
		response.setHeader("Content-Length", String.valueOf(arrReturn.length));

		sos.write(arrReturn);
	}

	// request 파싱 처리 결과 리스트에서 파일 데이터를 제외한 일반 KEY/VALUE 값을 추출하여 HashMap에 저장
	public void getParamValueMap(List<?> fileItemList, HashMap<String, String> paramMap) throws UnsupportedEncodingException
	{
		FileItem 			fileItem = null;				// file item

		// request 파싱 처리 결과 리스트에서 파일 데이터를 제외한 일반 KEY/VALUE 값을 추출하여 HashMap에 저장
		Iterator<?> iter1 = fileItemList.iterator();
		while (iter1.hasNext()) {
			fileItem = (FileItem)iter1.next();

		    // 일반 Form 필드 값을 추출하여 paramMap HashMap에 저장
		    if (fileItem.isFormField()) {
		    	String fieldName = fileItem.getFieldName();
		    	String fieldValue = fileItem.getString();

		    	// EXCEL_GLOBAL_INFO값등을 인코딩을 해서 올리므로 디코딩함(EXCEL_GLOBAL_INFO값에 한글이 있는 경우관련)
		    	fieldValue = URLDecoder.decode(fieldValue, "UTF-8");
		    	paramMap.put(fieldName, fieldValue);

		    	logMsg("fieldName = [" + fieldName + "], fieldValue = [" + fieldValue + "]");
		    }
		}
	}

	public void setResponseCommonHeader(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException
	{
		String		origin = "";
		boolean		bCorsHeader = true;				// cors_header 사용할지 여부

		browserType = getBrowserType(request);
	    logMsg("browserType: " + browserType);

        // Set cross domain response header
		response.setHeader("Access-Control-Allow-Credentials", "true");
		response.setHeader("Access-Control-Allow-Headers", "X-Requested-With");

		// a String containing the value of the requested header, or null if the request does not have a header of that name
		origin = request.getHeader("Origin");
		logMsg("origin = " + origin);

		if (origin == null) {
			response.setHeader("Access-Control-Allow-Origin", "*");
		}
		else {
			response.setHeader("Access-Control-Allow-Origin", origin);
		}
	}

	public void setResponseHeader(HttpServletResponse response, String saveAsFileName, long fileSize) throws UnsupportedEncodingException
	{
		response.setHeader("Content-Length", String.valueOf(fileSize));
		response.setHeader("Content-Transfer-Encoding", "binary");
        response.setHeader("Accept-Ranges", "bytes");
        response.setHeader("Set-Cookie", "fileDownload=true; path=/");

        /*
        // "Content-disposition: attachment"은
		// 브라우저 인식 파일확장자를 포함하여 모든 확장자의 파일들에 대해, 다운로드시 무조건 "파일 다운로드" 대화상자가 뜨도록 하는 헤더속성이라 할 수 있다.
        if(userAgent.indexOf("Safari") > -1) {
			response.setHeader("Content-Disposition", "attachment; filename=" + excelFileName);
		} else {
			response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + excelFileName);
        }
        */

        if (browserType.contains("IE")) {
        	saveAsFileName = URLEncoder.encode(saveAsFileName, "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-Disposition", "attachment;filename=" + saveAsFileName + ";");
        }
        else if (browserType.contains("FIREFOX")) {
        	saveAsFileName = new String(saveAsFileName.getBytes("UTF-8"), "ISO-8859-1");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + saveAsFileName + "\"");
        }
        else if (browserType.contains("OPERA")) {
        	saveAsFileName = new String(saveAsFileName.getBytes("UTF-8"), "ISO-8859-1");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + saveAsFileName + "\"");
        }
        else if (browserType.contains("CHROME")) {
        	saveAsFileName = new String(saveAsFileName.getBytes("UTF-8"), "ISO-8859-1");
            response.setHeader("Content-Disposition", "attachment; filename=\"" + saveAsFileName + "\"");
        }
        else if (browserType.contains("SAFARI")) {
            response.setHeader("Content-Disposition", "attachment; filename=" + saveAsFileName);
        }
	}

	public String getBrowserType(HttpServletRequest request) {
		String browser = "";
		String userAgent = request.getHeader("User-Agent");

		logMsg("user-agent:" + userAgent);

		if (userAgent.indexOf("Trident") > 0 || userAgent.indexOf("MSIE") > 0) {
	 		browser = "IE";
		}
		else if (userAgent.indexOf("Opera") > 0) {
	 		browser = "OPERA";
		}
		else if (userAgent.indexOf("Firefox") > 0) {
	 		browser = "FIREFOX";
		}
		else if (userAgent.indexOf("Safari") > 0) {
	 		if (userAgent.indexOf("Chrome") > 0) {
	  			browser = "CHROME";
	 		}
	 		else {
	  			browser = "SAFARI";
	 		}
		}

		return browser;
	}

	public String getContextRootPath(HttpServletRequest request)
	{
		String contextRootDirAbsolutePath = "";
		String resPath = "";
		String temPath = "";
		ServletContext context = request.getServletContext();

		// get context real path
		contextRootDirAbsolutePath = context.getRealPath("/");

		// Spring Boot에서는 getRealPath()로 값을 구할 수 없으므로 다른방식으로 구해줌
		if (contextRootDirAbsolutePath == null) {
			temPath = XDrmUtil.class.getProtectionDomain().getCodeSource().getLocation().getPath();

			// 앞에 붙은 file:/를 삭제
			resPath = temPath.substring(temPath.indexOf(":") + 2);

			resPath = resPath.substring(0, resPath.lastIndexOf("/"));
			resPath = resPath.substring(0, resPath.lastIndexOf("/"));
			resPath = resPath.substring(0, resPath.lastIndexOf("/"));
			resPath = resPath.substring(0, resPath.lastIndexOf(".")); // 폴더 이름으로 사용하기 위해 파일 확장자 제거
			resPath += "/";

			contextRootDirAbsolutePath = resPath;
		}

		return contextRootDirAbsolutePath;
	}

	// get a file upload process
	private ServletFileUpload getFileUploadProcessor()
	{
		// create a new file item factory
		DiskFileItemFactory factory = new DiskFileItemFactory();

		// create a new file object for a temporary directory
		File	tempDir = new File(tempDirAbsolutePath);

		// create a temporary directory
		if (!tempDir.exists()) {
			tempDir.mkdirs();
		}

		// set a temporary directory
		factory.setRepository(tempDir);

		// set a maximum memory file size
		factory.setSizeThreshold(maxMemoryFileSize);

		// create a servlet file upload object using a factory
		ServletFileUpload upload = new ServletFileUpload(factory);

		// set a maixmum file size
		upload.setSizeMax(maxFileSize);

		// set header encoding characterset for hangul file name
		upload.setHeaderEncoding("UTF-8");

		return upload;
	}

	// 임시 파일 이름 생성하여 리턴
	private String getRandomFileName()
	{
		return java.util.UUID.randomUUID().toString().replace("-", "");
	}

	// 파일 절대 경로를 이용하여 디렉토리 생성
	public void makeDirUsingFilePath(String fileAbsolutePath)
	{
		File 	oFile = new File(fileAbsolutePath);
		File	oDir = oFile.getParentFile();

		// create a diectory
		if (!oDir.exists()) {
			oDir.mkdirs();
		}
	}

	// 파일 절대 경로에 해당하는 파일 삭제
	public boolean deleteFile(String fileAbsolutePath)
	{
		boolean bDeleteSuccess = false;
		File 	file;

		try {
			file = new File(fileAbsolutePath);

			// 파일이 존재할 경우, 파일 삭제 처리
			if (file.exists()) {
				bDeleteSuccess = file.delete();
			}
		}
		catch (Exception e) {
			logMsg("deleteUploadFile e = " + e.getMessage());
		}
		catch (Throwable ex) {
			logMsg("deleteUploadFile ex = " + ex.getMessage());
		}

		return bDeleteSuccess;
	}

	// temp 폴더 파일 삭제 되었는지 체크 후 로그 출력
	public void checkTempFileDeleted(FileItem fileItem)
	{
		String	tempFileAbsolutePath = "";
		File 	tempFile = null;
		String 	tempFileName;

		DeferredFileOutputStream dfos = null;

		try {
			dfos = (DeferredFileOutputStream)fileItem.getOutputStream();

			tempFileName = dfos.getFile().getName(); // temp 폴더 파일의 이름 구하기
	        tempFileAbsolutePath = tempDirAbsolutePath + File.separator + tempFileName;

			tempFile = new File(tempFileAbsolutePath);
			if (tempFile.exists()) {
				// 파일이 존재할 경우 삭제 실패 한 것
				logMsg("Failed to delete file in the temp folder, path = " + tempFileAbsolutePath);
			}
			else {
				logMsg("Successfully delete files in the temp folder, path = " + tempFileAbsolutePath);
			}
		}
		catch (Exception e) {
			logMsg("checkTempFile e = " + e.getMessage());
		}
		catch (Throwable ex) {
			logMsg("checkTempFile ex = " + ex.getMessage());
		}
		finally {
			if (dfos != null) {
	    		try { dfos.close(); }
	    		catch (Exception ignore) {}
			}
		}
	}

	// FileItem에 대한 파일 데이터를 파일로 저장하고, FileItem 값 삭제 및 저장된 파일 경로 반환
	public String handleUploadFile(FileItem fileItem, HashMap<String, String> paramMap) throws Exception, Throwable
	{
		// FileItem의 파일 데이터를 저장할 파일 절대 경로
		String		saveFileAbsolutePath = "";

		// FileItem에 저장된 HTML FILE NAME을 구함
		String 		filePath = fileItem.getName();

		fileName = FilenameUtils.getName(filePath);

		String 		fileNameExt = FilenameUtils.getExtension(fileName);

		logMsg("filePath = " + filePath);
		logMsg("fileName = " + fileName);
		logMsg("fileNameExt = " + fileNameExt);

		logMsg("getContentType = " + fileItem.getContentType());
		logMsg("getSize = " + fileItem.getSize());

		// 파일 저장 디렉토리 절대 경로(saveBaseDirAbsolutePath) 및 랜덤 파일 이름을 생성하여 저장할 파일 경로를 설정
		saveFileAbsolutePath = saveBaseDirAbsolutePath + File.separator + getRandomFileName();
		logMsg("saveFileAbsolutePath = " + saveFileAbsolutePath);

		// 저장 파일 절대 경로를 이용하여 디렉토리 생성
		makeDirUsingFilePath(saveFileAbsolutePath);

		// 파일에 대한 fileItem 값을 파일에 저장, 오류 발생시 5회 반복
		while (true) {
			int	retryCount = 0;

			try {
				// FileItem의 파일 데이터를 파일로 저장
				fileItem.write(new File(saveFileAbsolutePath));
				break;
			}
			catch (Exception e) {
				logMsg("Exception Msg = " + e.getMessage());
				retryCount++;

				if (retryCount > 5) {
					logMsg("Fail to wirte a file");
					errorMsg = "Fail to wirte a file";

					// 파일이 존재할 경우, 파일 삭제 처리
					deleteFile(saveFileAbsolutePath);

					return null;
				}
				else {
	        		try { Thread.sleep(1000); }
	        		catch (InterruptedException ignore) { }
					continue;
				}
			}
		}

		logMsg("Success To Write File");

		// File 아이템의 파일 데이터 값 삭제 처리
		fileItem.delete();

		// temp 폴더의 임시파일 삭제 유무 로그 출력
		checkTempFileDeleted(fileItem);

		// 저장돤 파일 절대 경로 반환
		return saveFileAbsolutePath;
	}
}