본문 바로가기
작은 모듈(IT구슬)

선택한 것만 미리보기 하면서 멀티 파일 전송하기

by IT여행자 2020. 6. 3.
728x90

사용자가 파일을 다중 선택하거나 탐색기에서 파일을 드래그 앤 드롭을 사용하여 파일을 선택하도록 한 후 최종적으로 전송할 파일을 선택 또는 취소하여 파일을 전송하는 솔류션입니다.

 

그 흔한 jquery를 사용하지도 않았습니다. 자바스크립트의 기본 기능만을 사용했습니다. 물론 jquery를 사용한다면 소스가 조금 단순해지긴 할 것 같네요~ ^^

 

먼저 움짤을 보고 가시죠~

 

멀티 파일 전송 과정[움짤]

보다 다양한 기능은 처리하지 않았지만, file 태그를 사용하여 파일을 선택하거나, 탐색기에서 파일을 다중으로 선택할 수있고, 최종 선택된 이미지를 보면서 최종 선택 여부를 결정하여 파일을 전송할 수 있습니다.

 

[체크사항]

servlet.com에서 재공하고 있는 cos.jar API는 사용할 수 없습니다. 이 글이 포스팅되는 시점에서도 아직 file 태그의 multiple 속성을 지원하지 않습니다.

 

결론적으로 사용방법은 대단히 단순합니다. 첨부된 자바스크립트 모듈을 적당한 폴더에 저장한 후 아래와 같은 코드만 입력하면 끝입니다.

 

// html 문서가 있는 위치에 파일이 있음
<script src='file_upload.js'></script>
...
<script>
	upload.start('btnAtt', 'submitAction', 'imgs_wrap', 'file_upload_action.jsp');
</script>

 

upload.start(1, 2, 3, 4) 함수의 매개변수 설명 들어갑니다.

 

  1. 파일 선택 버튼의 id값입니다. file 태그의 속성 중 'multiple' 속성을 추가하면 파일을 다중 선택할 수 있습니다.
  2. 파일 전송 버튼의 id값입니다. 
  3. 첨부된 이미지들이 표시될 영역의 id값입니다. 
  4. 전송된 파일을 저장하는 서버 파일명입니다. 물론 예제는 jsp 파일 형식으로 되어 있지만 PHP, Spring, java bean 상관없습니다.(PHP는 테스트해 보지 못함)

물론 기타 CSS나 자바스크립트의 이벤트 처리는 이곳에서 사용하고 있는 버튼의 change, click을 제외하고는 모두 가능합니다. 솔류션의 전체 코드는 아래와 같습니다.

 

/**
 * http://jobtc.tistory.com/
 * 자바 스크립트를 통한 파일선택, 파일 드래그를 하면 파일 미리보기 기능 및
 * 서버로 파일 전송
 * date : 2020.06
 * author : jobtc.
 * 파일명 : file_upload.js 
 */  


(upload = function(){
	$id = function(id){ return document.getElementById(id) }
	
	var sel_files = [];
	var btnChooseID; //파일 선택 버튼
	var btnSendID; // 전송 버튼
	var appendZone; // 이미지가 표시될 영역
	var sendURL;
	var img_style = 'width:200px;height:150px;margin:4px';
	var chk_style = 'position:absolute; right:7px;bottom:7px;width:20px;height:20px;opacity:.7';
	var div_style = 'display:inline-block; position:relative';
	upload.start = function(chooseID, sendID, apZone, url){
		btnChooseID = $id(chooseID);
		btnSendID = $id(sendID);
		appendZone = $id(apZone);
		sendURL = url;
	
		// file tag로 파일을 선택한 경우
		btnChooseID.onchange = function(e){
			var files = e.target.files;
			var fileArr = Array.prototype.slice.call(files);
			for(f of fileArr){
				upload.imageLoader(f);
			}
		}

		// drap and drop을 사용한 경우
		appendZone.addEventListener('dragenter', function(e) {
			e.preventDefault();
			e.stopPropagation();
		}, false)
		appendZone.addEventListener('dragover', function(e) {
			e.preventDefault();
			e.stopPropagation();
		}, false)
		appendZone.addEventListener('drop', function(e) {
			e.stopPropagation();
			e.preventDefault();
			var dt = e.dataTransfer;
			var files = dt.files;
			for(f of files) upload.imageLoader(f);
		}, false)
		
		
		// 이미지 로드
		upload.imageLoader = function(file){
			sel_files.push(f);
			
			var reader = new FileReader();
			reader.readAsDataURL(f);
			reader.onload = function(ee){
				let img = document.createElement('img');
				img.setAttribute('style', img_style);
				img.src = ee.target.result;
				appendZone.append(img_div(img));
			}
		}
		
		// 파일 전송
		btnSendID.onclick = function(e){
			var data = new FormData();
			var uploadChk= document.getElementsByClassName('upload_chk');
			
			for(i=0 ; i<sel_files.length ; i++){
				if(uploadChk[i].checked){
					var name = 'image_';
					data.append(name, sel_files[i]);
				}
			}
			
			var xhr = new XMLHttpRequest();
			xhr.open("POST", sendURL);
			xhr.send(data);
			xhr.onreadystatechange = function(){
				if(this.status==200 && this.readyState == 4){
					console.log("OK.");
				}
			}
			
		}
		
		// 이미지 개당 div 생성
		img_div = function(img){
			var div = document.createElement('div');
			div.setAttribute('style', div_style);
			var btn = document.createElement('input');
			btn.setAttribute('type', 'checkbox');
			btn.setAttribute('class', 'upload_chk');
			btn.setAttribute('checked', 'checked');
			btn.setAttribute('style', chk_style);
			div.appendChild(img);
			div.appendChild(btn);
			return div;
		}
	}		

})()

 

웹페이지의 디자인이나 요소는 필요에 따라 서로 다를 수 있지만 본 포스팅에서는 단순히 파일만 업로드하는 태그들만 만들도록 하겠습니다.

 

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>upload.html</title>
<style>
.imgs_wrap{
	min-height:200px;
	border:1px solid #888;
}

</style>
<script src='../js/file_upload.js'></script>
</head>
<body>
	<h2>이미지 미리보기 | 멀티 업로드 TEST</h2>
	<div class='input-wrap'>
		<input type='file' id='btnAtt' multiple />
	</div>
	<div class='imgs_wrap' id='imgs_wrap'>
	</div>
	<input type='button' value='멀티파일 전송' id='submitAction'/>

	
	<script>
		upload.start('btnAtt', 'submitAction', 'imgs_wrap', 'file_upload_action.jsp');
	</script>
</body>
</html>

<script/> 부분의 upload.start() 함수의 파라미터 값과 HTML 부분의 태그 id값을 연결하면서 코드를 살펴보시기 바랍니다.

 

마지막으로 전송된 파일을 저장하는 JSP 페이지입니다. JSP 페이지에서 사용하는 파일 업로드 API는 아파치 재단에서 제공하고 있는 commons-fileupload API를 사용하였습니다.

 

<%@page import="java.util.Date"%>
<%@page import="org.apache.commons.fileupload.FileItem"%>
<%@page import="java.util.List"%>
<%@page import="java.io.File"%>
<%@page import="org.apache.commons.fileupload.servlet.ServletFileUpload"%>
<%@page import="org.apache.commons.fileupload.disk.DiskFileItemFactory"%>
<%@page import="java.io.FileOutputStream"%>
<%@ page language="java" contentType="text/text; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
	String saveDir = "N:/workspace/Lecture_javascript/WebContent/upload/";
	String tempDir = "c:/temp/";
	int maxSize = 1024*1024*50;
	String encoding = "utf-8";
	long fileLength = 0l;
	long filepos = 0l;
	String data = "";
	DiskFileItemFactory factory = new DiskFileItemFactory();
	factory.setSizeThreshold(4096);
	factory.setRepository(new File(tempDir) );
	
	ServletFileUpload sf = new ServletFileUpload(factory);
	sf.setHeaderEncoding("utf-8");
	sf.setFileSizeMax(maxSize);	
	
	List<FileItem> list = sf.parseRequest(request);
	for(FileItem fi : list) {
		String v = fi.getString("utf-8");
		String k = fi.getFieldName();
		
		
		if(fi.getSize()>0) {
			String f = fi.getName();
			String sysfile = new Date().getTime() + "-" +f ;
			out.print(f);
			File file = new File(saveDir + sysfile);
			fi.write(file);
			fi.delete();
		}
	}
%>

commons-fileupload API에서 파일을 다운로드 하는 과정은 크게 달라지지 않기 때문에 별다른 설명은 추가하지 않겠습니다. 

 

이것으로 포스팅을 마치도록 하겠습니다.

 

궁금하신 사항이 있으면 댓글 남겨 주시기 바랍니다.