참고사이트 : https://www.hanumoka.net/2018/09/06/spring-20180906-spring-file-upload/
서버에 파일을 저장할 때 고려해야 할 사항들.
1. 파일업로드 방식 결정하기
POST 방식 or Ajax 방식
2. 파일이름 중복
파일시스템에 파일을 저장할 때 파일이름이 중복되면 덮어써지게 된다. => UUID사용으로 해결
3. 파일 저장경로
개수가 많아지면 속도저하문제 발생 => 파일이 업로드되는 시점(날짜)으로 구분해서 폴더생성
업로드
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<title>파일 업로드 테스트</title>
</head>
<body>
<form action="/uploadTest/uploadFormAction" method="post"
enctype="multipart/form-data">
<input type="file" name="uploadFile" multiple />
<button>Submit</button>
</form>
</body>
</html>
업로드버튼 클릭
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<title>업로드 성공</title>
</head>
<body>
업로드 성공<br>
<c:forEach var="img" items="${list}">
<img src="/resources/upload/${img}" />
</c:forEach>
</body>
</html>
//<input type="file" name="uploadFile" multiple />
//MultipartFile : 스프링 프레임워크에서 제공하는 타입
@PostMapping("/uploadFormAction")
public String uploadFormPost(MultipartFile[] uploadFile, Model model) {
// (1)파일 저장할 위치 설정
String uploadFolder = "D:\\A_TeachingMaterial\\6.JspSpring\\workspace\\springProj\\src\\main\\webapp\\resources\\upload";
List<String> list = new ArrayList<String>();
for(MultipartFile multipartFile : uploadFile) {
logger.info("-----------");
logger.info("파일명 : " + multipartFile.getOriginalFilename());
logger.info("파일크기 : " + multipartFile.getSize());
// uploadFolder\\gongu03.jpg으로 조립
// 이렇게 업로드 하겠다라고 설계
File saveFile = new File(uploadFolder, multipartFile.getOriginalFilename());
try {
//파일 실제 명을 list에 담음
list.add(multipartFile.getOriginalFilename());
//transferTo() : 물리적으로 파일 업로드가 됨
multipartFile.transferTo(saveFile);
}catch(Exception e) {
logger.info(e.getMessage());
}//end catch
}//end for
//list : 파일명들이 들어있음
model.addAttribute("list", list);
//forward
return "uploadTest/uploadSuccess";
}
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="/resources/js/jquery-3.6.0.js"></script>
<title>Upload with Ajax</title>
<script type="text/javascript">
//파일을 담아두는 변수
var sel_file = [];
$(function(){
//----------------이미지 미리보기 시작----------------
$("#input_img").on("change", handleImgFileSelect);
function handleImgFileSelect(e){
//e.target : 파일 객체
//e.target.files : 파일객체 안의 파일들
var files = e.target.files;
//파일을 잘라서 array로 만든다.
var filesArr = Array.prototype.slice.call(files);
//f: 파일 객체
filesArr.forEach(function(f){
//미리보기는 이미지만 가능함
if(!f.type.match("image.*")){
alert("이미지만 가능합니다.");
return;
}
//파일객체 복사
sel_file.push(f);
// 파일을 읽어주는 객체 생성
var reader = new FileReader();
reader.onload = function(e){
//하나일 때
//$("#img").attr("src", e.target.result);
//forEach 반복 하면서 img 객체 생성
//반복될 때마다 img가 새로 생성된다.
var img_html = "<img src=\"" + e.target.result + "\" />";
$(".img_wrap").append(img_html);
}
reader.readAsDataURL(f);
});
}
//----------------이미지 미리보기 끝----------------
//모든 파일명.확장자(exe|sh|zip|alx)는 업로드를 못하도록 막기
//첨부파일의 확장자가 exe, sh, zip, alz 경우 업로드를 제한
var regex = new RegExp("(.*?)\.(exe|sh|zip|alx)$");
//최대 5MB까지만 업로드 가능
var maxSize = 5242880; //5MB
//확장자, 크기 체크
function checkExtension(fileName, fileSize){
if(fileSize >= maxSize){
alert("파일 사이즈 초과");
return false;
}
if(regex.test(fileName)){
alert("해당 종류의 파일은 업로드할 수 없습니다.");
return false;
}
//체크 통과
return true;
}
//Upload 버튼 클릭 시 수행
$("#uploadBtn").on("click", function(e){
//FormData : 가상의 <form> 태그
//Ajax를 이용하는 파일 업로드는 FormData를 이용
var formData = new FormData();
//<input type="file" 요소
var inputFile = $("input[name='uploadFile']");
//<input type="file" 요소 내의 이미지들
var files = inputFile[0].files;
// console.log(files);
for(var i=0; i<files.length;i++){
console.log(files[i]);
//확장자, 크기 체크
//function checkExtension(fileName, fileSize){
if(!checkExtension(files[i].name, files[i].size)){//!true라면 실패
return false;
}
formData.append("uploadFile", files[i]);
}
//없어?카드가?또?
// 아리가또 아포가또
//processData, contentType은 반드시 false여야 전송됨
$.ajax({
url:'/uploadTest/uploadAjaxAction',
processData: false,
contentType: false,
data: formData,
type:'POST',
success: function(result){
//JSON을 string으로 변환해서 console에 찍어보기
console.log("result : " + JSON.stringify(result));
}
});
});
});
</script>
</head>
<body>
<h1>Upload with Ajax</h1>
<div class="uploadDiv">
<input type="file" id="input_img" name="uploadFile" multiple>
</div>
<button id="uploadBtn">Upload</button>
<div class="img_wrap">
</div>
</body>
</html>
//<form> 태그를 이용하던 방식과 동일한 방식으로 처리됨
//Ajax 방식으로 결과 데이터를 전달하므로 Model을 사용하지 않음.
@ResponseBody
@PostMapping("/uploadAjaxAction")
public List<AttachFileVO> uploadAjaxAction(MultipartFile[] uploadFile) {
String uploadFolder = "D:\\A_TeachingMaterial\\6.JspSpring\\workspace\\springProj\\src\\main\\webapp\\resources\\upload";
//연/월/일 폴더 생성 시작-------
File uploadPath = new File(uploadFolder, getFolder());
logger.info("uploadPath : " + uploadPath);
logger.info("uploadPath.exists() : " + uploadPath.exists());
if(uploadPath.exists()==false) {//해당 경로가 없으면 생성해줘야함
uploadPath.mkdirs();
}
//연/월/일 폴더 생성 끝-------
//첨부된 파일의 이름을 담을 List
List<AttachFileVO> list = new ArrayList<AttachFileVO>();
for(MultipartFile multipartFile : uploadFile) {
logger.info("-----------");
logger.info("파일명 : " + multipartFile.getOriginalFilename());
logger.info("파일크기 : " + multipartFile.getSize());
AttachFileVO attachFileVO = new AttachFileVO();
// 1) fileName 세팅
attachFileVO.setFileName(multipartFile.getOriginalFilename());
//-----------UUID 파일명 처리 시작 ----------------------------
//동일한 이름으로 업로드되면 기존 파일을 지우게 되므로 이를 방지하기 위함
UUID uuid = UUID.randomUUID();
String uploadFileName = uuid.toString() + "-" + multipartFile.getOriginalFilename();
// c:\\upload\\gongu03.jpg으로 조립
// 이렇게 업로드 하겠다라고 설계 uploadFolder -> uploadPath
File saveFile = new File(uploadPath,uploadFileName);
//-----------UUID 파일명 처리 끝 ----------------------------
try {
//transferTo() : 물리적으로 파일 업로드가 됨
multipartFile.transferTo(saveFile);
//2) uploadPath
attachFileVO.setUploadPath(uploadPath.getPath());
//3) uuid
attachFileVO.setUuid(uuid.toString());
//-------썸네일 처리 시작---------
//이미지 파일인지 체킹
if(checkImageType(saveFile)) {
logger.info("이미지 파일? true");
//4) image여부
attachFileVO.setImage(true);
//uploadPath : 연/월/일이 포함된 경로
//uploadFileName : UUID가 포함된 파일명
FileOutputStream thumbnail =
new FileOutputStream(
new File(uploadPath,"s_"+uploadFileName));
Thumbnailator.createThumbnail(multipartFile.getInputStream(),
thumbnail, 100, 100);
thumbnail.close();
}else {
logger.info("이미지 파일? false");
}
//-------썸네일 처리 끝---------
//파일 객체(AttachFileVO)를 list에 담음
list.add(attachFileVO);
}catch(Exception e){
logger.info(e.getMessage());
}//end catch
}//end for
return list;
}//end uploadAjaxAction
//첨부파일을 보관하는 폴더를 연/월/일 계층 형태로 생성하기 위함
private String getFolder() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = new Date();
String str = sdf.format(date);
return str.replace("-", File.separator);
}
//특정한 파일이 이미지 타입인지 검사해주는 메소드
private boolean checkImageType(File file) {
try {
//file.toPath() : 파일의 전체 경로
logger.info("file.toPath() : " + file.toPath());
String contentType = Files.probeContentType(file.toPath());
logger.info("contentType : " + contentType);
//contentType이 image로 시작하면 이미지 타입이므로 true를 리턴함
return contentType.startsWith("image");
}catch(IOException e) {
e.printStackTrace();
}
return false;
}