엑셀 연동 방식에 대한 개요 및 구조는 아래와 같습니다. 연동 방식에 대한 변경은 실행 파라미터에 의해서 결정됩니다.
구분 | WAS 서버 연동 방식 | 로컬 엑셀 오토메이션 방식 | 로컬 스크립트 방식 |
---|---|---|---|
기반 기술 |
|
|
|
xFrame5 실행 환경 |
|
|
|
실행 환경 |
|
|
|
처리 속도 |
|
|
|
서버 부하 |
|
|
|
파일 선택 및 저장 경로 |
|
|
|
구분 | WAS 서버 연동 방식 | 로컬 엑셀 오토메이션 방식 | 로컬 스크립트 방식 |
---|---|---|---|
엑셀 파일 업로드 DRM 해제 |
|
|
기본적으로 DRM 해제 불가로 인해 엑셀 업로드 동작 오류 발생 |
엑셀 파일 다운로드 DRM 적용 |
|
|
기본적으로 엑셀 파일 저장시 DRM 적용 안됨 |
고객사 보안 솔루션 정책 확인 |
| xFrame5 전용 브라우저에서 파일 다운로드 보안 허용 | 범용 브라우저/xFrame5 전용 브라우저에서 파일 다운로드 보안 허용 |
구분 | WAS 서버 연동 방식 | 로컬 엑셀 오토메이션 방식 | 로컬 스크립트 방식 |
---|---|---|---|
엑셀 업로드 절차 |
|
|
|
실행 파라미터 | GRID_EXCELUPLOAD_METHODTYPE 파라미터
| GRID_EXCELUPLOAD_METHODTYPE 파라미터
| GRID_EXCELUPLOAD_METHODTYPE 파라미터
|
구분 | WAS 서버 연동 방식 | 로컬 엑셀 오토메이션 방식 | 로컬 스크립트 방식 |
---|---|---|---|
엑셀 다운로드 절차 |
|
|
|
실행 파라미터 | GRID_EXCELDOWNLOAD_METHODTYPE 파라미터
| GRID_EXCELDOWNLOAD_METHODTYPE 파라미터
| GRID_EXCELDOWNLOAD_METHODTYPE 파라미터
|
세부 옵션 실행 파라미터 | XEXCEL_DOWNLOAD_URL 파라미터
| ||
엑셀 파일 비밀번호 설정 |
|
|
구분 | WAS 서버 연동 방식 | 로컬 엑셀 오토메이션 방식 | 로컬 스크립트 방식 |
---|---|---|---|
엑셀 업로드 파일크기 |
|
|
|
실행 파라미터 | GRID_EXCELUPLOAD_MAXSIZE 파라미터
|
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; } }