지난번에 파일 업로드 프로그레스바 구현에 대한 글을 포스팅 한적이 있었습니다. 당시에는 단일 파일 업로드만 구현 했는데

이번에는 다중 파일 업로드 구현에 대해서 써볼까 합니다. 단일 파일 업로드 기능을 구현할때도 그랬지만 웹에서 파일업로드

기능 구현은 거의 해본적이 없습니다. 보통 OCX를 통한 파일 업로드 방식만  많이 사용해서 봐서 삽질을 많이 했네요.

일단 구현전에 괜찮은 파일 업로드 모듈을 찾아 봤습니다. 아무래도 제가 직접 제작하는것도 좋지만 저보다 뛰어난 분들이

만든 소스가 더 믿음직스러우니까요. 하지만 찾아보시면 보통 JS + Flash나 Flex를 사용한 파일 업로드 모듈을 보시게

될겁니다. 일반 PC환경에서면 크게 거슬리진 않지만 상황이 많이 나아지긴 했어도 모바일 환경하에서는 Flash 플레이어를

설치하지 않는 경우가 훨씬 많고 따로 설치 해야하는것도 은근히 번거롭게 느껴졌습니다. 그리고 요즘에 출시되는

스마트폰들은 성능이 좋지만 제가 현재 쓰고 있는 갤럭시U 모델의 경우 일반 웹도 버겁게 느껴질때가 많기 때문에 Flash나

Flex를 사용한 모듈은 배제하고 찾아보기로 했습니다. 그리고 다양한 기능보다는 파일업로드에 중점을 맞춘 모듈을 찾아보게

됐는데..

첫번째로 찾게 된것은 위와 같습니다. 제가 원했던 심플한 기능이었고 가장 맘에 들었던 것은 sumit 부분은 알아서 컨트롤 할 수

있게 되있다는 것 이었습니다. 그러나 input file을 css로 디자인 하게 되면 기능이 제대로  실행되지 않는다는 글을 보게 되서

쓰지 않게 됐습니다. (확실한 정보는 아닙니다.) 또 하나 찾게 됐던것은..


위와 같은 모듈입니다. (http://blueimp.github.com/jQuery-File-Upload/) 하지만 위 모듈의 경우 모바일 환경에서 일단 UI

때문에도 그냥 사용할 수도 없고 소스는 받아봤지만 커스터마이징 해서 쓰기에 시간이 너무 오래 걸릴것 같아 포기했습니다.

기능 자체는 상당히 깔끔하고 좋습니다. 한개 더 찾은 것 중에 쓸만하다 생각된것이 바로 아래와 같습니다. UI도 수정하기

크게 까다롭지 않고 플러그인 JS자체도 분석하기 수월 했습니다만.. 특정 ID를 가진 Submit버튼을 두고 전송을 해야해서

저의 경우 jQuery.form 의 ajaxSubmit()을 사용해야 할 필요가 있어서 패스하게 됐습니다.

보통은 찾아봤던 모듈들이 위와 같이 파일 업로드 자체만의 기능을 가지고 구현되어 있어서 다른 기능들에 포함해서

구현하려면 우선 모듈을 분석하는것이 먼저라 복잡하게 느껴져서 그냥 전부 패스 하고 직접 만들어 보게 됐습니다.

처음에 구현하려고 생각했던것이 위와 같은 형태입니다. '파일' 버튼(input type 'file'이 스타일링 된것)을 누르면 업로드

리스트가 추가되고 전송 버튼을 눌러 전송하는 형태 입니다. 위와 같이 구현하기 위해서는 가장 귀찮은 것이 파일추가 input

을 스타일링 하는 것입니다. 위 버튼의 경우 input file바깥의 div에 absolute position을 줘서 초과범위는 hidden 시키는

방법으로 input file을 숨겨놓은 형태입니다. 두번째로 문제가 되는것은 업로드할 파일 리스트 입니다. 업로드를 구현하는

back end(JSP,JAVA,PHP)는 구현하기 나름이겠지만 아래의 리스트에 파일 목록만 보일뿐 전송시에 input file의 value를

건네주지 못해서 구현 하나마나가 됩니다. 그러면 구현시 input file을 동적으로 생성하고 뿌려준다음 input file의 value를

주면 되지 않을까가 바로 생각나겠습니다만 보안상 이유로 input file의 경우 readonly 입니다. 때문에 사용자가 직접 파일을

선택하여 추가한 상태가 아니라면 value를 직접 set하는 것은 불가능 합니다. 예제에서는 위와 같이 스타일링 된 파일 버튼으로

파일을 선택하면 input file이 다시 동일 위치에 생성되며 이전의 element는 숨겨지는 형태입니다. 따라서 '파일' 버튼으로

다섯번 파일을 선택했다면 눈에 보이진 않으나 input file element가 다섯개가 생성된 상태입니다. 해당 내용은 예제내의

$("div#debug").text() 를 사용해서 뿌려보시면 이해가 가실 겁니다. 전에 포스팅한 글에서 예제로 업로드를 수행하는

부분의 스프링 MVC컨트롤러를 추가해뒀으므로 이번에는 JSP만 올려놓도록 하겠습니다. 항상 그랬지만 구현이 목적으로

실사용에 문제가 될 수 있는 부분도 있습니다. 이렇게 구현할 수도 있겠다 하는 방법만 보시면 되겠습니다.


multiuprogress.jsp


구현하고 보니 웹이 많이 발전하긴 했으나 파일입출력에 있어서는 아직 지원이 미흡하다는 느낌이 많이 드네요.

여담으로 HTML5에서는 위와 같은 꼼수를 쓰지 않고도 다중 파일 업로드를 구현할 수 있어 무척 반갑네요.

<input type="file" value="" name="upload[]" multiple>

위와같이 multiple 속성 선언으로 input의 '찾아보기' 버튼을 눌렀을때의 윈도우 다이얼로그에서 여러개의 파일을 선택할 수

있습니다. 저는 파이어폭스만 테스트 해봤으나.. 최신버전의 파이어폭스, 사파리, 크롬에서 작동한다고 합니다.

파일 업로드 라이브러리 선택(COS,Apache Commons Upload)에 대해 포스팅 했던 글에 이어 이번에는 파일 업로드

구현 예제에 대해 올려보겠습니다. 파일 업로드시에 보통 작은 데이터의 경우 손쉽게 jQuery의 ajaxStart, ajaxStop function을

통해 AJAX 요청 시작시, 완료시에 대한 이벤트를 처리 할 수 있으므로 ajaxStart()시에 GIF이미지 등을 화면에 띄워줘서

처리 상태를 사용자에게 알릴 수 있습니다. 보통 큰 용량의 파일이 업로드 될때는 JSP등의 페이지로 처리 하기 보다는 별도의

업로드 모듈을 사용하는것이 낫겠지만 5MB~10MB내의 파일이 오갈때에는 굳이 그럴 필요없이 웹으로만 구현하는것도

충분하다고 생각합니다. PC의 경우 굳이 프로그레스바를 보여주는것 보다는 간단하게 위와 같은 방법으로 처리하는것이

나을지 모르겠으나 스마트폰의 전송속도는 3G망이나 4G나 일반 유선통신 속도와 비교할것이 못되기도 하고 잘 안터지는

지역에서는 스피너 이미지로 프로그레스바를 보이는것 만으로는 사용자가 작업 완료 여부를 알 수 없기 때문에 만들어

보게 됐습니다.


progressbarex.zip

※압축첨부한 예제 파일들의 경우 거의 그대로 적용하시면 실행 되실겁니다. JAVA파일의 경우 package를 환경에 맞게

추가 하시면 됩니다. 구현이 목적으로 성능등은 고민한 흔적같은 것 없습니다. 그냥 예제로 봐주세요.ㅋ

그리고 Spring MVC컨트롤러에 대한 이해가 없으신 분들은 *.do로 호출되는 기능들만 따로 떼서 구현해보셔도 되겠습니다.

예전에 JAVA의 Swing을 사용해서 Thread 구현하여 만들어본 프로그레스바 외에는 이제까지 웹으로 프로그레스바를

구현해본적이 없어서 바로 감이 오지는 않았고 사실 구현 전에는 적당한 예제 소스를 많이 검색해 봤습니다. 근데 괜찮다 싶은

모듈이어도 적용하기가 어려운 경우가 많고 모바일 웹 화면에서 사용하려면 UI도 수정해야 되는 경우가 많았고 대부분의

예제는 JSP → <form> action으로 서블릿 호출 → 서블릿에서 Apache Commons Upload를 통해

전송상태를 PrintWriter로 출력 하는 것이 대부분이었습니다. 또한 파일 업로드 자체는 XMLHttpRequest에서 지원하지

않아 jQuery.form 라이브러리를 사용한 ajaxForm()등을 사용하여 업로드 하는 방식을 많이 보게 됐는데 잘 적용되지

않았습니다. 저의 경우 Spring Web MVC를 사용하여 구현하고 있었는데 이 구조 특성상 request가 완료된후 페이지로

포워딩 하므로 중간에 프로그레스 상태를 listen하여 화면에 출력될때 어떻게 하면 좋을까 고민이 됐습니다. 한참 고민중에

세션이 생각이 나더군요. 제가 만든 예제의 대략적인 프로그레스바 출력 흐름은 다음과 같습니다.

progressbardemo.jsp 에서 파일 선택후 전송버튼 클릭시 AJAX로 fileupload.do

(스프링 컨트롤러(Controller.java)에 매핑된 주소) 호출 ,

호출시에 jQuery.ajaxStart()에서 uploadstatus.do를 호출하여 fileupload.do가 실행중의 프로그레스 상태를

얻어오기 위해 주기적으로 UploadProgressListener를 호출함

fileupload.do에서 Apache Commons Upload를 사용하여 업로드 처리 중

UploadProgressListener에 세션을 셋팅 →

UploadProgressListener의 update()메소드에서 업로드 진행상태를

세션에 셋팅해서 upstatus.jsp로 포워딩함 →

초기에 progressbardemo.jsp에서 ajaxStart()에서 uploadstatus.do가 호출될때 세션에 셋팅된 파일의 전송정보

(읽은 바이트수, 전체크기, 전송속도) 등을 JSON으로 출력함 →

전송완료후 clearInterval로 setInterval로 확인하던 업로드 상태를 중지시킴

jQuery가 AJAX요청시에 지원하는 표현 간단하면서 강력한 기능들이 많은 도움이 됐네요. 이걸 이전 방식으로 XMLHttpRequest

를 일일히 얻어와서 구현하는 형태로 만들었다면 소스량도 많아지고 많이 복잡했겠지요. 예제 내용은 사실 효율적으로 보면

매우 좋지 않습니다. 결과만 보기위해서 만들긴 했습니다만 일단 실 서비스에 적용해서 사용한다면 파일 업로드가 진행중일때

upstatus.jsp가 JSON String을 얻어오기 위해 계속해서 호출이 되므로 여러 사용자가 사용시 서버에 많은 부하가 걸릴 수

있습니다. 이 방법보다는 MVC컨트롤러가 호출될때 세션에 셋팅된 파일 전송 정보 JSON Object를 얻을수 있는 방법을

전에 어느분의 블로그에서 본것 같은데 기억이 가물하네요. 찾아서 한번 적용해 봐야겠습니다.

또한 Spring Framework 자체에서 Apache Commons Upload를 Injection해서 사용할 수 있는 방법도 봤습니다만

일단은 익숙해진 방법으로 구현했습니다. 좀 더 효율적인 방법으로 수정해서 사용하실 분은 공유 해주셨으면 좋겠네요.

보통 파일 업로드 모듈이 Flash나 Flex를 사용한 환경이 많아 모바일 환경에서 사용자체는 가능하나 잘 맞지 않고 현재 제가

만든 게시판의 경우 하이브리드 앱 형태로 웹뷰를 사용한 앱위에서 웹페이지를 호출하는 형태인데 자료를 찾다보니 이 또한

안드로이드 앱 자체에서 업로드 진행상태 구현 방법에 대해서만 많이 나와 있어서 구현해 보게 됐습니다. 다운로드의 경우

에도 구현할수는 있겠지만 보통 다운로드의 경우 브라우저에서 진행상태를 보여주는 경우가 많으므로 생략했습니다.

좀 더 효율적인 방법 제시해주시는 분들은 언제나 환영입니다^^