# Spring - 파일업로드 연습1 (일반적인 방식)
# Spring - 파일업로드 연습2 (파일명 중복제거)
# Spring - 파일업로드 연습3 (업로드 결과를 iframe에 출력)
# AJAX(Asynchronous Javascript And XML)란?
이전 포스팅에서는 일반적인 방식으로 파일업로드를 구현했는데 이번에는 파일업로드를 ajax방식으로 구현해보자. 또한 날짜별로 저장 디렉토리를 자동 생성하여 업로드된 파일을 관리할 수 있도록 처리하고, 업로드된 파일이 이미지일 경우에는 썸네일을 생성해보자.
View(Ajax방식 - 업로드 페이지)
1. uploadAjax.jsp
<%@ 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>Insert title here</title>
<%@ include file="../include/header.jsp" %>
<style>
.fileDrop {
width:600px;
height: 200px;
border: 1px dotted blue;
}
small {
margin-left: 3px;
font-weight: bold;
color: gray;
}
</style>
<script>
$(document).ready(function(){
$(".fileDrop").on("dragenter dragover", function(event){
event.preventDefault(); // 기본효과를 막음
});
// event : jQuery의 이벤트
// originalEvent : javascript의 이벤트
$(".fileDrop").on("drop", function(event){
event.preventDefault(); // 기본효과를 막음
// 드래그된 파일의 정보
var files = event.originalEvent.dataTransfer.files;
// 첫번째 파일
var file = files[0];
// 콘솔에서 파일정보 확인
console.log(file);
// ajax로 전달할 폼 객체
var formData = new FormData();
// 폼 객체에 파일추가, append("변수명", 값)
formData.append("file", file);
$.ajax({
type: "post",
url: "${path}/upload/uploadAjax",
data: formData,
// processData: true=> get방식, false => post방식
dataType: "text",
// contentType: true => application/x-www-form-urlencoded,
// false => multipart/form-data
processData: false,
contentType: false,
success: function(data){
alert(data);
}
});
});
});
</script>
</head>
<body>
<%@ include file="../include/menu.jsp" %>
<h2>AJAX File Upload</h2>
<!-- 파일을 업로드할 영역 -->
<div class="fileDrop"></div>
<!-- 업로드된 파일 목록 -->
<div class="uploadedList"></div>
</body>
</html>
파일업로드 영역에 텍스트 파일 또는 이미지파일을 드래그했을 때 내용이 바로 보여지는 기본효과를 막기 위해서 jQuery에
event.preventDefault();
코드를 작성한다.
ajax로 전달할 폼객체를 생성하고 file을 추가 시켜준다. file을 전달할 때는 ajax 옵션 속성을type:post
,processData: false
,contentType: false
로 설정한다.
2. 구현화면
업로드할 파일을 업로드 영역에 드래그한 뒤 업로드처리 완료되면 성공메시지 alert알림
크롬 개발자도구에서 업로드된 파일정보 확인
저장 디렉토리에서 원본이미지파일과 썸네일이미지이 생성되었는지 확인
Controller - 업로드 관련 컨트롤러
UploadController
@Controller
public class UploadController {
private static final Logger logger = LoggerFactory.getLogger(UploadController.class);
@RequestMapping(value="/upload/uploadAjax", method=RequestMethod.GET)
public void uploadAjax(){
// uploadAjax.jsp로 포워딩
}
// produces="text/plain;charset=utf-8" : 파일 한글처리
@ResponseBody
@RequestMapping(value="/upload/uploadAjax", method=RequestMethod.POST, produces="text/plain;charset=utf-8")
public ResponseEntity<String> uploadAjax(MultipartFile file) throws Exception {
logger.info("originalName : "+file.getOriginalFilename());
logger.info("size : "+file.getSize());
logger.info("contentType : "+file.getContentType());
return new ResponseEntity<String>(UploadFileUtils.uploadFile(uploadPath, file.getOriginalFilename(), file.getBytes()), HttpStatus.OK);
}
}
파일 한글 처리를 위해서
@RequestMapping
어노테이션 속성에produces="text/plain;charset=utf-8"
을 추가한다.
업로드 관련 클래스
1. UploadFileUtils 클래스
public class UploadFileUtils {
public static String uploadFile(String uploadPath, String originalName, byte[] fileData) throws Exception {
// UUID 발급
UUID uuid = UUID.randomUUID();
// 저장할 파일명 = UUID + 원본이름
String savedName = uuid.toString() + "_" + originalName;
// 업로드할 디렉토리(날짜별 폴더) 생성
String savedPath = calcPath(uploadPath);
// 파일 경로(기존의 업로드경로+날짜별경로), 파일명을 받아 파일 객체 생성
File target = new File(uploadPath + savedPath, savedName);
// 임시 디렉토리에 업로드된 파일을 지정된 디렉토리로 복사
FileCopyUtils.copy(fileData, target);
// 썸네일을 생성하기 위한 파일의 확장자 검사
// 파일명이 aaa.bbb.ccc.jpg일 경우 마지막 마침표를 찾기 위해
String formatName = originalName.substring(originalName.lastIndexOf(".")+1);
String uploadedFileName = null;
// 이미지 파일은 썸네일 사용
if (MediaUtils.getMediaType(formatName) != null) {
// 썸네일 생성
uploadedFileName = makeThumbnail(uploadPath, savedPath, savedName);
// 나머지는 아이콘
} else {
// 아이콘 생성
uploadedFileName = makeIcon(uploadPath, savedPath, savedName);
}
return uploadedFileName;
}
// 날짜별 디렉토리 추출
private static String calcPath(String uploadPath) {
Calendar cal = Calendar.getInstance();
// File.separator : 디렉토리 구분자(\\)
// 연도, ex) \\2017
String yearPath = File.separator + cal.get(Calendar.YEAR);
System.out.println(yearPath);
// 월, ex) \\2017\\03
String monthPath = yearPath + File.separator + new DecimalFormat("00").format(cal.get(Calendar.MONTH) + 1);
System.out.println(monthPath);
// 날짜, ex) \\2017\\03\\01
String datePath = monthPath + File.separator + new DecimalFormat("00").format(cal.get(Calendar.DATE));
System.out.println(datePath);
// 디렉토리 생성 메서드 호출
makeDir(uploadPath, yearPath, monthPath, datePath);
return datePath;
}
// 디렉토리 생성
private static void makeDir(String uploadPath, String... paths) {
// 디렉토리가 존재하면
if (new File(paths[paths.length - 1]).exists()){
return;
}
// 디렉토리가 존재하지 않으면
for (String path : paths) {
//
File dirPath = new File(uploadPath + path);
// 디렉토리가 존재하지 않으면
if (!dirPath.exists()) {
dirPath.mkdir(); //디렉토리 생성
}
}
}
// 썸네일 생성
private static String makeThumbnail(String uploadPath, String path, String fileName) throws Exception {
// 이미지를 읽기 위한 버퍼
BufferedImage sourceImg = ImageIO.read(new File(uploadPath + path, fileName));
// 100픽셀 단위의 썸네일 생성
BufferedImage destImg = Scalr.resize(sourceImg, Scalr.Method.AUTOMATIC, Scalr.Mode.FIT_TO_HEIGHT, 100);
// 썸네일의 이름을 생성(원본파일명에 's_'를 붙임)
String thumbnailName = uploadPath + path + File.separator + "s_" + fileName;
File newFile = new File(thumbnailName);
String formatName = fileName.substring(fileName.lastIndexOf(".") + 1);
// 썸네일 생성
ImageIO.write(destImg, formatName.toUpperCase(), newFile);
// 썸네일의 이름을 리턴함
return thumbnailName.substring(uploadPath.length()).replace(File.separatorChar, '/');
}
// 아이콘 생성
private static String makeIcon(String uploadPath, String path, String fileName) throws Exception {
// 아이콘의 이름
String iconName = uploadPath + path + File.separator + fileName;
// 아이콘 이름을 리턴
// File.separatorChar : 디렉토리 구분자
// 윈도우 \ , 유닉스(리눅스) /
return iconName.substring(uploadPath.length()).replace(File.separatorChar, '/');
}
}
파일을 한 디렉토리에 계속 저장해 파일이 많이지면 나중에 검색속도가 느려지고, 파일관리가 쉽지가 않다. 그렇기 때문에 파일을 업로드할 때마다 날짜별(연,월,일)로 디렉토리를 생성하도록 처리해주었다.
calcPath()
메서드에서 날짜별 디렉토리를 추출하고,makeDir()
메서드로 디렉토리를 생성한다.
업로드된 파일이 이미지 파일일 경우에는 뷰에서 미리보기 화면을 보여주기 위해 썸네일 이미지를 생성한다. 원본파일의 확장자명을 추출하고, 변수formatName
에 저장하여 MeidaUtils클래스에서 이미지 파일 여부를 확인하여 리턴한다. 리턴된 변수formatName
이 null이 아니면makeThumbnail()
메서드에서 썸네일을 생성하고, null일 경우makeIcon()
메서드에서 아이콘을 생성한다.
2. MediaUtils 클래스
public class MediaUtils {
private static Map<String, MediaType> mediaMap;
// 자동로딩
static {
mediaMap = new HashMap<String, MediaType>();
mediaMap.put("JPG", MediaType.IMAGE_JPEG);
mediaMap.put("GIF", MediaType.IMAGE_GIF);
mediaMap.put("PNG", MediaType.IMAGE_PNG);
}
public static MediaType getMediaType(String type) {
return mediaMap.get(type.toUpperCase());
}
}
UploadFileUtils클래스에서 추출한 파일의 확장자명을 대문자로 변환하고,
mediaMap
에 담긴 값을 호출한 뒤 리턴한다. 3가지(jpg,gif,png) 이미지 파일일 경우에는 값이 복사되어 리턴되지만, 아닐경우에는 null상태로 리턴된다.