[java]Dropzone.js 이미지&파일 다중 업로드 예제

Dropzone.js란

드래그 앤 드롭 이벤트가 가능한 javascript 라이브러리이다.
완전한 오픈소스이며 웹사이트에서 서버에 파일을 쉽게 올릴 수 있는 것이 장점이다.
또한 다중 멀티 업로드도 지원한다.
 
 

Dropzone.js  기본사용법

<script src="https://code.jquery.com/jquery-latest.min.js"></script>
<script src="https://unpkg.com/dropzone@5/dist/min/dropzone.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/dropzone@5/dist/min/dropzone.min.css" type="text/css" />
<div id="fndropzone" class="dropzone"></div>
<script>
    Dropzone.autoDiscover = false;
    $(function () {
        $("#fndropzone").dropzone({
            url : "/dropzone/upload",
            maxFilesize : 50,
            paramName: "file"
        });
    });
</script>

 
Jquery를 이용해 드랍존을 생성했다.
우선 드랍존을 생성할 태그에 id 값을 선언해 주고. dropzone 클래스를 추가해 준다.
그리고 내가 필요한 옵션을 추가한다.

 

주요 옵션 

옵션 설명
url 업로드 url
method request 메소드 선택
headers 요청 헤더 설정
autoProcessQueue 자동으로 보내기
clickable 클릭 가능 여부
autoQueue 바로 서버에 전송할지 설정
createImageThumbnails 파일 업로드시 썸네일 생성 여부
thumbnailHeigth 썸네일 높이 사이즈
thumbnailWidth 썸네일 가로 사이즈
maxFiles 업로드 파일 수
maxFilesize 파일 최대 업로드 용량
paramName form 데이터에서 이름 기본 설정은 'file'
parallelUploads 동시에 업로드할 파일수
uploadMultiple 멀티 업로드 사용 여부
timeout 커넥션 타임아웃
addRemoveLinks 업로드 후 파일 삭제 버튼 생성 여부
dictRemoveFile 삭제버튼 표시 텍스트
acceptedFiles 가능한 이미지 파일 확장자

 
 
Dropzone에서 사용 가능한 모든 옵션 목록

 

Configuration Options | Dropzone

If null, no capture type will be specified If camera, mobile devices will skip the file selection and choose camera If microphone, mobile devices will skip the file selection and choose the microphone If camcorder, mobile devices will skip the file selecti

docs.dropzone.dev

 

이벤트 핸들러

this.on("drop", function(e) {
 
});

this.on("dragstart", function(e) {
 
});

this.on("dragend", function(e) {
 
});

this.on("dragenter", function(e) {
 
});

this.on("dragover", function(e) {
 
});

this.on("dragleave", function(e) {
 
});

this.on("addedfile", function(file) {
 
});

this.on("processing", function(file) {
 
});

this.on("removedfile", function(file) {
 
});

this.on("thumbnail", function(file, dataURL) {
 
});

this.on("error", function(file) {
 
});

this.on("uploadprogress", function(file, progress, bytesSent) {
 
});

this.on("sending", function(file, formData) {
 
});

this.on("success", function(file) {
 
});

this.on("complete", function(file) {
 
});

this.on("canceled", function(file) {
 
});

this.on("maxfilesreached", function(file) {
 
});

this.on("maxfilesexceeded", function(file) {
 
});

this.on("processingmultiple", function(fileList) {
 
});

this.on("sendingmultiple", function(fileList) {
 
});

this.on("successmultiple", function(fileList) {
 
});

this.on("completemultiple", function(fileList) {
 
});

this.on("canceledmultiple", function(fileList) {
 
});

this.on("reset", function(e) {
 
});

this.on("queuecomplete", function(e) {
 
});

this.on("totaluploadprogress", function(uploadProgress, totalBytes, totalBytesSent) {
 
});

 

멀티 업로드 예제

<div class="content">
    <form id="imageForm" class="smart-form" enctype="multipart/form-data">
            <div id="fndropzone" class="dropzone" ></div>
            <div style="text-align: right;margin-top: 10px;margin-right: 10px;">
                <button type="submit" id="btn_upload" class="btn btn-primary">업로드 </button>
            </div>
    </form>
</div>
<script type="text/javascript">

    Dropzone.autoDiscover = false;

    $(function () {
        $("#fndropzone").dropzone({
            url : "/dropzone/upload",
            paramName : "file",
            uploadMultiple : true,
            addRemoveLinks : true,
            parallelUploads: 20,
            autoProcessQueue : false,
            maxFilesize : 50,
            maxFiles : 10,
            acceptedFiles: '.jpeg,.jpg,.png',
            dictResponseError : 'Error uploading file!',
            init : function() {
                const myDropzone = this;
                $("button[type='submit']").click(function(e) {
                    console.log('submit');
                    e.preventDefault();
                    e.stopPropagation();

                    if (myDropzone.getQueuedFiles().length <= 0 ) {
                        alert('업로드할 파일이 없습니다.');
                        return false;
                    } else {
                        console.log("lng", myDropzone.getQueuedFiles().length, myDropzone.getUploadingFiles());
                        if(myDropzone.getQueuedFiles().length > 0) {
                            myDropzone.processQueue();
                        }
                    }
                });
                this.on("successmultiple", function(file, response) {
                    console.log('on successmultiple');
                    let json_obj = JSON.stringify(response);
                    console.log('json_obj : '+ json_obj);
                    try {
                        if (typeof json_obj === 'string') {
                            json_obj = JSON.parse(json_obj);
                            var result = json_obj.result;
                            console.log('result :', result);
                            if(result == 'success') {
                                var imageNames = json_obj.imageNames;
                                for (var num in imageNames) {
                                    var imageName = imageNames[num];
                                    console.log('imageName :', imageName);
                                }
                            }
                        }

                    } catch (e) {
                        console.error('Error parsing JSON:', e);
                        alert('에러가 발생하였습니다. 관리자에게 문의하세요.');
                        return;
                    }
                });
                this.on("addedfile", function(file) {
                    file.previewElement.addEventListener("click", function() {
                        myDropzone.removeFile(file);
                    });
                });
                this.on("error", function(response) {
                    console.log('error : '+ response);
                });
            }
        });
    });
</script>

form의 enctype을 multipart/from-data로 변경하고 드랍존이 생성될 div 밑에 버튼을 하나 추가해 줬다.

그리고 업로드 버튼을 눌러야만 이미지가 업로드되게 하기 위해 autoProcessQueue(자동업로드) 옵션은 false로 변경했다.
그리고 여러 개의 파일을 동시에 업로드하기 위해  uploadMultiple 옵션은 true로 변경한다.

버튼을 누르면 업로드할 파일이 있는지(getQueuedFiles().length )를 확인하고 파일이 존재하면 파일의 업로드를 진행한다.

파일이 업로드된 후에는 this.on("successmultiple", function(file, response) { 를 이용해 업로드된 이미지명을 json 형태로 받을 수 있게 하였다.
 

@Controller
public class DropZoneController {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Value("${nas.dir}")
    public String nasRoot;

    @Value("${dropzone.file.dir}")
    public String fileRoot;

    @RequestMapping(value = "/dropzone")
    public String dropzone() {
         log.info("/dropzone ");
        return "dropzone/upload";
    }

    @RequestMapping(value = "/dropzone/upload")
    public @ResponseBody String upload(@RequestParam Map<String, String> rParam, final HttpServletRequest request) throws Exception {

        log.info("/dropzone/upload  rParam : " + rParam + " getContentType : " + request.getContentType());

        Gson gson = new Gson();
        HashedMap resultMap = new HashedMap();
        String result ="failure";

        ArrayList<String> imageNames = new ArrayList<String>();
        String uploadFolder = nasRoot + fileRoot;

        File file = new File(uploadFolder);
        if (!file.exists()) {
            file.mkdirs();
        }

        String dateFolder = getFolder();
        File uploadPath = new File(uploadFolder , dateFolder);

        if (!uploadPath.exists()) {
            uploadPath.mkdirs();
        }
        try {
            for (Part part : request.getParts()) {
                if (part.getSize() > 0) {
                   String fileExtension = FilenameUtils.getExtension(part.getSubmittedFileName());
                    String fileName =  String.valueOf(Calendar.getInstance().getTimeInMillis()) +"."+fileExtension;
                    log.info("fileName : " + fileName);
                    part.write(uploadFolder + dateFolder + fileName);
                    log.info(uploadFolder + dateFolder + fileName);
                    imageNames.add(fileRoot+ dateFolder + fileName);
                }
            }

            if(imageNames.size() > 0){
                result="success";
            }

        } catch (Exception e) {
            e.printStackTrace();
            result = "failure";
        }

        resultMap.put("result", result);
        resultMap.put("imageNames", imageNames);

        return gson.toJson(resultMap);
    }

    private String getFolder(){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/");
        Date date = new Date();
        String str = sdf.format(date);
        return str;
    }
}

파일이 업로드되면 request.getParts()를 이용해 업로드 파일의 개수만큼 파일을 업로드한다.
파일명은 Calendar.getInstance(). getTimeInMillis()을 이용해 현재시간을 1/100초 단위의 변환값으로 사용했다.
업로드할 파일이 많다면 업로드 파일 폴더 안에 달/일 기준의 폴더들을 생성해 업로드해 준다.
 

아주 간단하게 dropzone.js를 이용해 멀티 업로드를 구현해 보았다.