2023-08-15
요즘엔 웹에서 파일을 업로드 할 때, 업로드 파일이 이미지이면 바로 미리보기를 제공해 주거나, 파일을 선택하자 마자 바로 서버에 업로드 해주는 식의 직관적이고 사용자에게 편한 라이브러리들이 많이 생겼다.
이번에 포스팅 할 것은 그런 파일 업로드 라이브러리중 하나인 Dropzone이다.
일단 직접 예제부터 한번 보자.
<link rel="stylesheet" href="/post_inc/dropzone/dropzone.min.css">
<script src="/post_inc/dropzone/dropzone.min.js"></script>

<style>
.dropzone {
	border: 2px dashed #e3e6ed;
	padding: 0;
}
.dropzone .dz-remove .remove_btn {
	color: white;
	font-size: 25px;
	font-weight: bold;
	cursor: pointer;
	text-shadow: 3px 3px 10px black;
}
.dropzone .dz-remove {
	position: absolute;
	top: 5PX;
	right: 5PX;
	z-index: 99;
}
.dropzone .dz-progress {
	display: none;
}

.show_files, #show_arr1{
	border:1px solid black;
}
</style>

<div class="dropzone" id="drop_file"></div>
<button id="upload_btn1">업로드 하기</button>
<div class="show_files"></div>

<script>
var up_files = []
var dz1 = new Dropzone('#drop_file', {
	url: "/tmp1_ajax", // 사용하지 않아도 무조건 선언되어 있어야 함
	autoProcessQueue: false, // true : 파일 업로드 되자마자 서버로 요청, false : 서버에는 올라가지 않은 상태. 따로 this.processQueue() 호출시 전송
	addRemoveLinks: true, // 업로드 후 파일 삭제버튼 표시 여부
	dictRemoveFile: '<div class="remove_btn"> x </div>', // 삭제 버튼
	dictDefaultMessage: 'Upload Zone<br>Click here!!<br>Drag file!!', // 업로드 지역 표시 문구
	parallelUploads: 20, // 동시 파일 업로드수
	removedfile: function (file) {
		file.previewElement.remove();
	},
	init: function () {
		this.on('addedfile', function (file) { // file:파일소스
			up_files.push(file)
		});
	},

});

$(function(){
	$(document).on('click', '#upload_btn1', function(){
		for(var k in up_files){
			// console.log(up_files[k])
			var tmp1 = "name:"+up_files[k].name+"("+up_files[k].size+"byte)<br>"
			$('.show_files').append(tmp1)
		}
	})
})
</script>
일단 Dropzone에 대한 자세한 옵션들은 아래를 참조 하길 바란다
홈페이지: https://www.dropzone.dev/
옵션파일: https://github.com/dropzone/dropzone/blob/main/src/options.js

그럼 위 예제를 간단히 설명하자면, 파일을 여러개 올려 놓고 미리보기를 제공 한채로 놔뒀다가 한번에 업로드를 시키는 예제이다.
일단 바로 업로드를 하지 않기 위해서 autoProcessQueue옵션을 false로 해놓고, 파일이 업로드 될때마다(addedfile) up_files에 해당 파일의 원본 소스를 넣어 놓는다.
그 후에 버튼을 누르면 해당 원본 소스를 자기 입맛에 맞게 가지고 놀면 되는 것이다.



//----------------------- 2024. 01. 29 추가

dropzone을 좀 더 사용하다 보니 좀더 편리하고 쉽게 사용하는 방법들이 생겨서 추가해 놓는다.
<div class="dropzone" id="drop_file2"></div>
<button id="dz_btn1">미리보기 초기화</button>
<div id="show_arr1"></div>

<script>
var dz2 = null

dz2 = new Dropzone('#drop_file2', {
	url: '/a_ajax', // 사용하지 않아도 무조건 선언되어 있어야 함
	maxFiles: 4, // 최대 미리보기 수
	autoDiscover: false,
	autoProcessQueue: false,
	addRemoveLinks: true,
	dictRemoveFile: '<div class="remove_btn"> x </div>',
	dictDefaultMessage: '여기를 클릭해 주세요!',
	removedfile: function (file) {
		// console.log(dz2)
		show_files('removedfile')
		file.previewElement.remove();
	},
	init: function () {
		this.on('addedfile', function (file) {
			// console.log(dz2)
			show_files('addedfile')
		});
		this.on("maxfilesexceeded", function (file) {
			// console.log(dz2)
			show_files('maxfilesexceeded')
			file.previewElement.remove();
			return false;
		});
	},
});

function show_files(a){
	$('#show_arr1').html(a+' ------------ <br><br>')
	$.each(dz2.files, function(k,v){
		$('#show_arr1').append('status: '+v.status+', file_name: '+ v.name+', size:'+v.size+'<br>')
	})
}

$(document).on('click', '#dz_btn1', function(){
	dz2.removeAllFiles()
	show_files('removeAllFiles')
})
</script>
우선 예제는 위와 같이 파일을 바로 backEnd로 넘기지 않고, 넘기기 전에 클라이언트에서 최대한 처리한다는 것이 같지만, 파일을 따로 변수에 저장하는 것이 아니라, dz2.files변수를 직접 사용하는게 다르다.
그리고 파일의 제한을 maxFiles에서 제한해 두면, maxfilesexceeded에서 제어가 가능한데, 이곳에서 maxFiles의 값을 넘어가면 dz2.files에서 status가 error라고 표시되기 때문에, file.previewElement.remove()을 써주면 미리보기 파일이 maxFiles의 값 이상 표시 안해주도록 하는 것이 가능해진다.

이런식으로 클라이언트의 뷰를 처리한 뒤 아래와 backEnd로 넘겨주는 file값들을 아래와 같이 처리해서 ajax로 넘기면 되는것이다.
let tmp_form = new FormData();

// status가 queued인 파일들만 저장해서 ajax한다
$.each(dz2.files, function (k, v) {
	if( v.status == "queued"){
		tmp_form.append('file_' + String(k), v);
	}
})

$.ajax({
	type: "post"
	, url: "/a_ajax"
	, data: tmp_form
	, processData: false
	, contentType: false
	, success: function (data) {
		// ajax후에는 미리보기 부분을 초기화 시켜준다.
		dz2.removeAllFiles( true )
	}
	, error: function (xhr, option, error) {
		console.log(xhr);
	}
})