Spring Framrwork

Spring Framework File Upload with Drag and Drop

gregorio 2018. 3. 16. 09:09

프로젝트를 수행하면서 파일 업로드는 다양한 방식으로 개발이 가능하다.


파일 업로드 시 업로드할 파일을 Drag and Drop 방식으로 File을 브라우저에 첨부한 후 업로드하는 방식에 대해 설명하고자 한다.


인터넷을 검색하면 Drag and Drop의 다양한 파일 업로드 방식이 있으나, 적용 시 손쉽게 적용할 수 있는 방법을 소개한다.


먼저 파일을 업로드하는 Jsp파일이다.


■ fileUpload.jsp 전체 소스

<html>

<head>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>

<style>

#dragandrophandler

{

border:2px dotted #0B85A1;

width:400px;

color:#92AAB0;

text-align:left;vertical-align:middle;

padding:10px 10px 10 10px;

margin-bottom:10px;

font-size:200%;

}



.progressBar {

    width: 200px;

    height: 22px;

    border: 1px solid #ddd;

    border-radius: 5px; 

    overflow: hidden;

    display:inline-block;

    margin:0px 10px 5px 5px;

    vertical-align:top;

}

 

.progressBar div {

    height: 100%;

    color: #fff;

    text-align: right;

    line-height: 22px; /* same as #progressBar height if we want text middle aligned */

    width: 0;

    background-color: #0ba1b5; border-radius: 3px; 

}

.statusbar

{

    border-top:1px solid #A9CCD1;

    min-height:25px;

    width:700px;

    padding:10px 10px 0px 10px;

    vertical-align:top;

}

.statusbar:nth-child(odd){

    background:#EBEFF0;

}

.filename

{

display:inline-block;

vertical-align:top;

width:250px;

}

.filesize

{

display:inline-block;

vertical-align:top;

color:#30693D;

width:100px;

margin-left:10px;

margin-right:5px;

}

.abort{

    background-color:#A8352F;

    -moz-border-radius:4px;

    -webkit-border-radius:4px;

    border-radius:4px;display:inline-block;

    color:#fff;

    font-family:arial;font-size:13px;font-weight:normal;

    padding:4px 15px;

    cursor:pointer;

    vertical-align:top

    }

</style>


<script>


var arrStatus = [];

var formDatas = [];

$(document).ready(function()

{

var obj = $("#dragandrophandler");

obj.on('dragenter', function (e) 

{

    e.stopPropagation();

    e.preventDefault();

    $(this).css('border', '2px solid #0B85A1');

});

obj.on('dragover', function (e) 

{

     e.stopPropagation();

     e.preventDefault();

});

obj.on('drop', function (e) 

{

 

     $(this).css('border', '2px dotted #0B85A1');

     e.preventDefault();

     var files = e.originalEvent.dataTransfer.files;

 

     //We need to send dropped files to Server

     handleFileUpload(files,obj);

});

$(document).on('dragenter', function (e) 

{

    e.stopPropagation();

    e.preventDefault();

});

$(document).on('dragover', function (e) 

{

  e.stopPropagation();

  e.preventDefault();

  obj.css('border', '2px dotted #0B85A1');

});

$(document).on('drop', function (e) 

{

    e.stopPropagation();

    e.preventDefault();

});

$("#upload").click(function(e){

e.preventDefault();

alert("send file to server");

for (var i = 0; i < arrStatus.length; i++) {

debugger;

sendFileToServer(formDatas[i], arrStatus[i]);

}

    });  

});

function sendFileToServer(formData,status)

{

    var uploadURL ="http://localhost:8080/sample/fileupload.do"; //Upload URL

    var extraData ={path:"d:/temp/files", }; //Extra Data.

    formData.append("path", "d:/temp/files");

    formData.append("saveName", "log_org.zip");

    

    debugger;

    var jqXHR=$.ajax({

            xhr: function() {

            var xhrobj = $.ajaxSettings.xhr();

            if (xhrobj.upload) {

                    xhrobj.upload.addEventListener('progress', function(event) {

                        var percent = 0;

                        var position = event.loaded || event.position;

                        var total = event.total;

                        if (event.lengthComputable) {

                            percent = Math.ceil(position / total * 100);

                        }

                        //Set progress

                        status.setProgress(percent);

                    }, false);

                }

            return xhrobj;

        },

    url: uploadURL,

    type: "POST",

    contentType:false,

    processData: false,

        cache: false,

        data: formData,

        success: function(data){

            status.setProgress(100);

 

            $("#status1").append("File upload Done<br>");         

        }

    }); 

 

    status.setAbort(jqXHR);

}

 

var rowCount=0;

function createStatusbar(obj)

{

     rowCount++;

     var row="odd";

     if(rowCount %2 ==0) row ="even";

     this.statusbar = $("<div class='statusbar "+row+"'></div>");

     this.filename = $("<div class='filename'></div>").appendTo(this.statusbar);

     this.size = $("<div class='filesize'></div>").appendTo(this.statusbar);

     this.progressBar = $("<div class='progressBar'><div></div></div>").appendTo(this.statusbar);

     this.abort = $("<div class='abort'>Abort</div>").appendTo(this.statusbar);

     obj.after(this.statusbar);

 

    this.setFileNameSize = function(name,size)

    {

        var sizeStr="";

        var sizeKB = size/1024;

        if(parseInt(sizeKB) > 1024)

        {

            var sizeMB = sizeKB/1024;

            sizeStr = sizeMB.toFixed(2)+" MB";

        }

        else

        {

            sizeStr = sizeKB.toFixed(2)+" KB";

        }

 

        this.filename.html(name);

        this.size.html(sizeStr);

    }

    this.setProgress = function(progress)

    {       

        var progressBarWidth =progress*this.progressBar.width()/ 100;  

        this.progressBar.find('div').animate({ width: progressBarWidth }, 10).html(progress + "% ");

        if(parseInt(progress) >= 100)

        {

            this.abort.hide();

        }

    }

    this.setAbort = function(jqxhr)

    {

        var sb = this.statusbar;

        this.abort.click(function()

        {

            jqxhr.abort();

            sb.hide();

        });

    }

}

function handleFileUpload(files,obj)

{

   for (var i = 0; i < files.length; i++) {

    //FormData

var fd = new FormData();

        fd.append('file', files[i]);

        formDatas[i] = fd;

 

        //Status bar

        var status = new createStatusbar(obj); //Using this we can set progress.

        status.setFileNameSize(files[i].name,files[i].size);

        arrStatus[i] = status;

        //sendFileToServer(fd,status);

 

   }

}


</script>

</head>

 

<body>

<div id="dragandrophandler">Drag & Drop Files Here</div>

<br><br>

<div>

<button type="button" id="upload" name="upload">upload</button>

</div>

<div id="status1"></div>


</body>

</html>


Drag and Drop으로 파일 업로드하는 두가지 방식이 있다.

인터넷을 검색하여 찾은 소스는 파일 선택 시 바로 업로드 하게 되어 있는데 변경한 방식은 UPLOAD 버튼을 선택 시 파일을 업로드 하게 재구성하였다.



Script의 handleFileUpload의 FormData와 status객체를 생성하여 sendFileToServer function을 바로 호출하면 파일을 Drag and Drop이 완료된 후 바로 파일을 업로드 한다.



■ Drag & Drop 시 서버로 파일 전송

  function handleFileUpload(files,obj)

{

   for (var i = 0; i < files.length; i++) {

var fd = new FormData();

        fd.append('file', files[i]);

 

        //Status bar

        var status = new createStatusbar(obj); //Using this we can set progress.

        status.setFileNameSize(files[i].name,files[i].size);

        sendFileToServer(fd,status);

 

   }

}



그러나 업무상으로 File을 Drag & Drop으로 첨부한 후 upload 버튼을 선택 시 파일을 서버로 전송하고자 하는 경우에는 Array Object를 생성하여 FormData와 status를 Array Object에 저장한 후 Upload 버튼 선택 시 파일을 업로드할 수 있다.


■ Drag & Drop 파일 객체 저장 방식

  function handleFileUpload(files,obj)

{

   for (var i = 0; i < files.length; i++) {

    //FormData

var fd = new FormData();

        fd.append('file', files[i]);

        formDatas[i] = fd;

 

        //Status bar

        var status = new createStatusbar(obj); //Using this we can set progress.

        status.setFileNameSize(files[i].name,files[i].size);

        arrStatus[i] = status;

 

   }

}


■ upload 버튼 Click 시 파일 전송

$("#upload").click(function(e){

e.preventDefault();

alert("send file to server");

for (var i = 0; i < arrStatus.length; i++) {

debugger;

sendFileToServer(formDatas[i], arrStatus[i]);

}

 


파일을 업로드 할 때 추가적인 정보를 같이 넘겨주는 경우가 있는데 파일을 저장하기 위해 FormData 객체를 사용하고 있기 때문에 FormData 의 append method를 이용하여 추가적인 정보를 같이 전달할 수 있다.


  function sendFileToServer(formData,status)

{

    var uploadURL ="http://localhost:8080/sample/fileupload.do"; //Upload URL

    var extraData ={path:"d:/temp/files", }; //Extra Data.

    formData.append("path", "d:/temp/files");

    formData.append("saveName", "log_org.zip");

    



다음은 서버 프로그램이다.


먼저 Fileupload 화면을 보여주는 Controller이다.


@RequestMapping(value="/sample/filePageView.do")

public ModelAndView filePageView() throws Exception {

ModelAndView mav = new ModelAndView();

mav.setViewName("fileUpload");

return mav;


ModelAndView 객체를 생성하여 fileUpload.jsp를 browser에 보여준다.


파일을 업로드하는 Controller이다.


@RequestMapping(value="/sample/fileupload.do", method = RequestMethod.POST)

    public @ResponseBody LinkedList<Map<String, Object>> upload(MultipartHttpServletRequest request, HttpServletResponse response,

    String path, String saveName) {

 

        //1. build an iterator

         Iterator<String> itr =  request.getFileNames();

         MultipartFile mpf = null;

 

         LinkedList<Map<String, Object>> files = new LinkedList<Map<String, Object>>();

         //2. get each file

         while(itr.hasNext()){

 

             //2.1 get next MultipartFile

             mpf = request.getFile(itr.next());

             LOGGER.debug(mpf.getOriginalFilename() +" uploaded! "+ files.size());

 

 

             //2.3 create new fileMeta

             Map<String, Object> fileMeta = new HashMap<String, Object>();

             fileMeta.put("fileName", mpf.getOriginalFilename());

             fileMeta.put("fileSize", mpf.getSize()/1024+" Kb");

             fileMeta.put("fileType", mpf.getContentType());

// 

             try {

//                fileMeta.setBytes(mpf.getBytes());

 

                 // copy file to local disk (make sure the path "e.g. D:/temp/files" exists)            

                 FileCopyUtils.copy(mpf.getBytes(), new FileOutputStream("D:/temp/files/"+mpf.getOriginalFilename()));

 

            } catch (IOException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

             //2.4 add to files

             files.add(fileMeta);

         }

        // result will be like this

        // [{"fileName":"app_engine-85x77.png","fileSize":"8 Kb","fileType":"image/png"},...]

        return files;

    } 


Chrome과 IE 11버전에서 정상적으로 실행되는것을 확인하였다.