대용량 파일 업로드
DEXTUpload 제품군에서 2GB 이상의 크기를 가지는 파일을 업로드하는 것을 '대용량 파일 업로드'라고 명명한다. DEXTUploadX5는 DEXTUploadNJ(Java 환경 기반)와 같은 자사의 서버 컴포넌트 제품을 함께 사용하여 대용량 파일 업로드를 처리할 수 있는 기능을 제공한다. (대용량 파일 업로드는 자사 컴포넌트와 연동을 한 경우만 가능하며, DEXTUploadX5 단독으로 지원하지 않는다.)
대용량 파일 업로드는 일반적인 파일 업로드와는 처리 방식이 다르다. 일반적인 파일 업로드에서는 파일 10개를 전송하기 위해 1번의 요청으로 10개 모두 전송할 수 있지만, 대용량 파일 업로드에서는 상황에 따라 최소 20번에서 수천 번의 요청을 서버로 보내기도 한다. DEXTUploadX5의 대용량 파일 업로드 기능은 기본적으로 이어올리기를 지원하고 있기 때문에, 제품이 사전에 서버 컴포넌트와의 통신하는 과정이 발생하며, 파일을 쪼개서 전송하기 때문에 나누어진 크기(청크)에 따라 요청 개수가 증가한다. 예를 들어 크기가 2.5GB인 파일 10개를 업로드한다고 가정하고, 약 10MB 크기의 청크로 전송을 한다면 약 2500회의 요청이 발생할 수 있다. 그러나 DEXTUploadNJ와 같은 서버 컴포넌트에서 쪼개진 파일들을 하나로 합치는 작업을 알아서 처리하고 있기 때문에, 개발자는 목적지에 파일을 저장(이관)하는 작업만 총 10회 정도 처리해주면 된다.
- DEXTUploadX5 대용량 업로드 설정 방법
-
대용량 업로드 서비스를 구현하려면 클라이언트 컴포넌트와 서버 컴포넌트 모두 대용량으로 업로드를 하겠다고 설정값을 변경해줘야 한다.
다음은 DEXTUploadX5 설정 예시이다.
// onDX5Created 이벤트 함수에서 대용량 업로드 설정을 한다. function onDX5Created(id) { var dx = dx5.get(id); // 업로드 경로를 설정한다. dx.setUploadURL("http://domain/path/extension-upload.do"); // 업로드 방식을 대용량으로 설정한다. dx.setUploadMode("EXTS"); // 파일을 분할하는 블록 크기를 바이트 단위로 설정한다. dx.setUploadBlockSize(10 * 1024 * 1024); } - DEXTUploadNJ 서버 컴포넌트 제품을 사용한 서버 측 설정
-
DEXTUploadNJ는 자바 JSP/서블릿 기반에서 파일 업로드를 처리하는 서버 전용 컴포넌트이다. DEXTUploadNJ는 대용량 업로드의 서버 측 처리를 하기 위한 기능이 포함되어 있으며, 자사의 DEXTUploadX5 클라이언트 제품군과 연동이 가능하다.
ExtensionFileUploadFilter 클래스는 javax.servlet.Filter 인터페이스를 구현한 클래스이다. 이 클래스는 Filter 클래스이므로 자바 서블릿 컨테이너(Tomcat 서버처럼)에 의해 자동으로 로드되며, 코드 수준의 생성 혹은 호출없이 대용량 파일 업로드를 처리한다.
다음은 DD(web.xml)에서 ExtensionFileUploadFilter 필터를 설정하는 방법이다.
<filter> <filter-name>extensionFilter</filter-name> <filter-class>devpia.dextuploadnj.support.common.ExtensionFileUploadFilter</filter-class> <!-- 설정 파라미터 생략 --> </filter> <filter-mapping> <filter-name>extensionFilter</filter-name> <!-- 대용량 업로드가 완료된 파일을 최종적으로 개발자 코드 수준에서 처리하기 위한 서블릿 혹은 URL 매핑 <servlet-name>서블릿이름</servlet-name> <url-pattern>URL 매핑</url-pattern> --> </filter-mapping>
- DEXTUploadNJ(2.5.0 이전 버전) 서버 컴포넌트 제품을 사용한 서버 측 처리(JSP/Servlet)
-
ExtensionFileUploadFilter 필터를 통하여 업로드가 처리된 파일 정보는 개발자가 작성해야할 서블릿 혹은 JSP에서 HttpServletRequest#getAttribute 메소드를 사용하여 업로드 결과를 얻을 수 있다.
// DEXTUPLOADNJ_EXTENSION_FILE_UPLOAD_RESULT를 키(key)로 사용하여 필터의 처리 결과를 가지고 있는 MultipartCollection 객체를 얻는다. MultipartCollection multiparts = (MultipartCollection)request.getAttribute(Definition.DEXTUPLOADNJ_EXTENSION_FILE_UPLOAD_RESULT); try { FileItem item = multiparts.getFileItem(0); if (item.isEmpty()) { // 파일이 비어 있는 경우에는 일반적으로 예외를 발생시켜야 한다. } // 실제 저장할 위치로 임시 파일을 저장(복사 혹은 이동)한다. // 인자로 주어진 디렉터리 경로가 없다면 ExtensionFileUploadFilter의 defaultRepository 파라미터 값으로 설정된 경로가 타겟이 된다. String path = item.save(); response.setCharacterEncoding("UTF-8"); response.setContentType("text/plain"); // 응답 객체에는 아무 값이나 작성해도 상관 없지만, 일반적으로 파일의 고유값을 나타내는 키(key)나 파일 이름 또는 경로를 작성한다. response.getWriter().write(path); } finally { if (multiparts != null) { // 리소스를 제거한다. 삭제가 되지 않은 임시 파일이 있다면 모두 삭제된다. multiparts.deleteTempFiles(); multiparts.clear(); } } - DEXTUploadNJ(2.5.0 이후 버전) 서버 컴포넌트 제품을 사용한 서버 측 처리(JSP/Servlet)
-
2.5.0 버전부터는 복잡한 사전 단계를 줄이고 FileUpload 클래스를 사용하여 파일 항목을 얻을 수 있다.
FileUpload dextnj = null; try { dextnj = new FileUpload(request); dextnj.prepare(); FileItem item = dextnj.getFileItem(); if (item.isEmpty()) { // 파일이 비어 있는 경우에는 일반적으로 예외를 발생시켜야 한다. } String path = item.save(); response.setCharacterEncoding("UTF-8"); response.setContentType("text/plain"); response.getWriter().write(path); } finally { if (dextnj != null) dextnj.close(); } - DEXTUploadNJ(2.6.0 이전 버전) 서버 컴포넌트 제품을 사용한 서버 측 처리(Spring Web Framework)
-
Spring 환경에서는 DD(web.xml)에서 ExtensionFileUploadFilter를 설정해야 할 뿐만 아니라, DispatcherServlet 객체의 설정을 담당하는 XML 파일에 MultipartResolver bean로 설정해주어야 한다.
# web.xml <servlet> <servlet-name>defaultDispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/dispatcher-servlet.xml</param-value> </init-param> </servlet>dispatcher-servlet.xml 파일을 열어 DEXTUploadNJMultipartResolver bean을 선언한다.
# dispatcher-servlet.xml <bean id="multipartResolver" class="devpia.dextuploadnj.support.spring.DEXTUploadNJMultipartResolver"/>
만약에 CommonMultipartResolver 또는 StandardServletMultipartResolver(Spring에서 제공하는)이 설정되어 있다면 해당 설정을 삭제해야 한다. 이 bean들은 DEXTUploadNJMultipartResolver bean과 동일한 작업을 수행하므로, 동시에 설정하는 경우 오류가 발생할 수 있다.
ExtensionFileUploadFilter 필터를 통하여 처리된 임시 파일 정보는 Controller에 매핑된 메소드에서 MultipartFile 객체로 받을 수 있고, 여기서 최종 저장 작업을 수행하면 된다.
@RequestMapping(value = "/extension-upload.do", method = RequestMethod.POST) public void upload( // DEXTUploadX5 제품인 경우 "DEXTUploadX5_FileData" 이름으로 전달된다. @RequestParam(value = "DEXTUploadX5_FileData") MultipartFile file, HttpServletResponse response) throws IOException { // FileItem 인터페이스로 캐스팅한다. FileItem item = (FileItem)file; if (item.isEmpty()) { // 파일이 비어 있는 경우에는 일반적으로 예외를 발생시켜야 한다. // DEXTUploadX5와 같은 클라이언트에서는 파일이 없는 요청을 서버로 보내지 않기 때문이다. } String path = item.save(); response.setCharacterEncoding("UTF-8"); response.setContentType("text/plain"); response.getWriter().write(path); } - DEXTUploadNJ(2.6.0 이후 버전) 서버 컴포넌트 제품을 사용한 서버 측 처리(Spring Web Framework)
-
DEXTUploadNJ 2.6.0 버전부터는 Spring 환경에서 사용하는 전용 필터인 DEXTUploadNJSpringExtensionUploadFilter를 제공한다. 이 필터를 사용하면 DEXTUploadNJMultipartResolver 설정을 생략해도 된다.
# web.xml <filter> <filter-name>extensionUploadFilter</filter-name> <filter-class>devpia.dextuploadnj.support.spring.DEXTUploadNJSpringExtensionUploadFilter</filter-class> <!-- 설정 파라미터 생략 --> </filter> # dispatcher-servlet.xml <!-- DEXTUploadNJMultipartResolver 설정은 생략해도 된다. -->
- 서버로부터 받은 응답 데이터 처리
-
서버 측 처리가 완료되면, DEXTUploadX5는 결과를 응답 데이터로 돌려 받는다. 이 응답 데이터는 컴포넌트의 getResponses 함수를 가지고 얻을 수 있다. 서버에서는 파일의 개수만큼 해당 코드가 호출되지만, 업로드 완료 이벤트는 한 번만 발생하므로 onDX5UploadCompleted 이벤트 함수는 한 번만 호출된다.
// onDX5UploadCompleted 함수는 업로드가 완료(서버 측 파일 업로드 처리가 완료)되면 호출되는 이벤트 함수이다. function onDX5UploadCompleted(id) { var responses = dx5.get(id).getResponses(); for (var i = 0, len = responses.length; i < len; i++) { console.log(responses[i]); } }