Archive for 5월 2012

파이어폭스에서 드래그앤드롭으로 파일업로드 구현하기

파이어폭스에서는 파일업로드를 드래그앤드롭으로 할 수 있습니다.

3.6 버전에서부터 지원했던 것 같습니다.

자바스크립트와 Spring 프레임워크를 사용해서 간단하게 파일 업로드 프로그램을 작성해 보았습니다.

HTML + JavaScript 업로드 구현부분 UI

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>파일업로드</title>
</head>
<body>
    <form name="form0" action="file/upload" method="POST" enctype="multipart/form-data">
        <ul>
            <li>파일 하나 업로드 : <input id="file1" type="file" name="file" /></li>
            <li>여러 파일 업로드 : <input id="file2" type="file" name="multifile" multiple /></li>
            <li>파일을 선택하고 '전송'을 눌러 업로드하시거나, 탐색기에서 파일을 드래그앤드롭하여 업로드할 수 있습니다. 드래그앤드롭은 '전송'을 누르는 것이 아니라 드롭시점에 바로 업로드됩니다.</li>
        </ul>
        <input type="submit" value="전송"/>
    </form>
    <script type="text/javascript">
    var dropzone1 = document.getElementById('file1');
    var dropzone2 = document.getElementById('file2');
    setDnDhandler(dropzone1);
    setDnDhandler(dropzone2);
    
    function setDnDhandler(obj) {
        obj.addEventListener("dragover", function(event) {
            event.preventDefault();
        }, true);
        obj.addEventListener("drop", function(event) {
            event.preventDefault();
            var allTheFiles = event.dataTransfer.files;
            for (var i=0; i<allTheFiles.length; i++) {
                var element = document.createElement('div');
                element.id = 'f' + i;
                document.body.appendChild(element);
                sendFile(allTheFiles[i], element.id);
            }
        }, true);
    }
    function sendFile(file, fileId) {
        var xhr = new XMLHttpRequest();
        
        xhr.upload.addEventListener("progress", function(e) {
                if (e.lengthComputable) {
                    var percentage = Math.round((e.loaded * 100) / e.total);
                    document.getElementById(fileId).innerHTML = file.name + ' - ' + percentage + '%';
                }
            }, false);
        xhr.onreadystatechange = function() {  
            if (xhr.readyState == 4 && xhr.status == 200) {
                alert(xhr.responseText);
            }
        };
        xhr.upload.addEventListener("load", function(e){
                document.getElementById(fileId).innerHTML = file.name + ' - uploaded';
            }, false);
        xhr.open("POST", "file/upload");
        var fd = new FormData();
        fd.append("file", file);
        xhr.send(fd);
    }
    </script>
</body>
</html>

드래그앤드롭을 사용할 때, 드롭될 영역의 경우는 input형이 아니라도 가능하지만, 일반 HTML전송도 가능하게 예제를 작성하고자 input을 사용하였습니다.

드래그앤드롭을 지원하는 부분은 addEventListener 부분입니다. “dragover”, “drop” 이벤트에 대하여 기본동작을 막고, 자바스크립트처리를 할 수 있도록 하였습니다.

파이어폭스에서 input에 type이 file이면, 기본동작이 파일이 업로드를 위해 준비됩니다. IE의 경우는 그 동작조차 없으며, 파일 열기가 수행되지요.

drop이벤트에서 XMLHttpRequest를 통해 AJAX업로드를 합니다.

“xhr.upload.addEventListener(“progress”, …);” 부분은 업로드 처리 경과를 확인할 수 있는 기능입니다. 업로드된 양을 퍼센트로 출력해 줍니다.

서버에서 파일 받기

package test.control;

import java.io.File;
import java.io.IOException;

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

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

@Controller
@RequestMapping("/file")
public class FileController {
    
    @RequestMapping("upload")
    public @ResponseBody String upload(HttpServletRequest request,
            HttpServletResponse response) throws IOException {
        if (! (request instanceof MultipartHttpServletRequest)) {
            response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Expected multipart request");
            return null;
        }
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        for ( String fileName : multipartRequest.getFileMap().keySet() ) {
            for ( MultipartFile file : multipartRequest.getFiles(fileName) ) {
                String originalFileName = file.getOriginalFilename();
                File destination = File.createTempFile("file", originalFileName, new File("/var/dgkim"));
            }
        }
        return "success";
    }
}

스프링 MVC 프레임워크를 통해, 저수준의 파일 처리부분은 빠지고 실제 파일명과 File객체 처리를 바로 할 수 있습니다.

위 예에서는 의미없이 임시 파일만 생성했으나, 필요에 따라 처리를 작성할 수 있겠습니다.

서버측 라이브러리는 Maven으로 관리하였으며, Spring 3.0외에 추가로 아래 의존성이 있습니다.

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>1.3</version>
        </dependency>

소스 다운로드 : http://www.dgkim.net/dav/users/dgkim/fileuptest.zip

참조