첨부파일은 어떻게 보낼 것이냐?
<form>태그를 이용해서 보낼수도 있고, Ajax를 이용해서 보낼수 있음.
서버에서는 첨부파일을 어떻게 처리하는가?
API를 이용해서 처리를 한다.
commons-fileupload : 가장 많이 사용, 서블렛 3.0 이전에도 사용
서블렛 3.0 이상 - 3.0이상부터는 자체적인 파일 업로드 처리 API 지원
web.xml 2.5버전이므로 변경한다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
id="WebApp_ID" version="3.1">
그리고 web.xml의 <servlet> 태그 안에 <multipart-config> 추가
<multipart-config>
<location>C:\\upload\\temp</location>
<max-file-size>20971520</max-file-size> <!-- 1MB * 20 -->
<max-request-size>41943040</max-request-size> <!-- 40MB -->
<file-size-threshold>20971520</file-size-threshold> <!-- 20MB -->
</multipart-config>
</servlet>
해석을 해보자면
<file-size-threshold>
- 특정 사이즈의 메모리 사용
<max-file-size>
- 업로드 되는 파일 최대 크기
<max-request-size>
- 한번에 올릴 수 있는 최대 크기
web.xml은 WAS 자체의 설정
스프링에서 업로드는 MultipartResolver라는 타입의 객체를 빈으로 등록해야 함.
enctype의 속성값을 'multipart/form-data'로 지정
<input type='file'>의 경우 클릭하면 하나만 전송하는데 최근 브라우저에서 'multiple'이라는 속성을 이용해서 한개의 태그로 여러 개의 파일을 업로드가 가능하다.
파일 처리는 스프링에서 어떻게 하냐?
MultipartFile이라는 타입을 사용
첨부파일이 여러 개 선택할 수 있으므로 배열 타입으로 설정한 후 파일을 업로드 한다.
MultipartFile의 메서드
String getName()
- 파라미터 이름, <input>태그 이름
String getOriginalFileName()
- 업로드된 파일 이름
boolean isEmpty()
- 파일이 존재하지 않는 경우
long getSize()
- 업로드되는 파일의 크기
byte[] getBytes()
- byte[]로 파일 데이터 반환
InputStream getInputStream()
- 파일데이터와 연결된 inputStream을 반환
transferTo(File file)
- 파일 저장
var formDate = new FormData();
가상의 <form> 태그
@PostMapping("/uploadAjaxAction")
public void uploadAjaxPost(MultipartFile[] uploadFile) {
log.info("update ajax post ...");
String uploadFolder = "D:\\upload";
for(MultipartFile multipartFile : uploadFile) {
log.info("------------");
log.info("Upload File Name : " + multipartFile.getOriginalFilename());
log.info("Upload File Size : " + multipartFile.getSize());
String uploadFileName = multipartFile.getOriginalFilename();
uploadFileName = uploadFileName.substring(uploadFileName.lastIndexOf("//")+1);
log.info("only file name : " + uploadFileName);
File saveFile = new File(uploadFolder, uploadFileName);
try {
multipartFile.transferTo(saveFile);
} catch (Exception e) {
log.error(e.getMessage());
}
}
File 클래스에서 두개의 파라미터를 받으면
File(String parent, String child)
- parent 폴더 경로의 child라는 파일에 대한 File 객체를 생성한다.
log.error는 로그 레벨이 INFO, ERROR 등등 더 많은 로그 레벨이 존재하는데.
구분을 하는 방법은 '의도'를 가지고 고려한다.
Exception이 발생하는 경우에 무의식적으로 ERROR 레벨을 사용하는 경우가 있는데, 이는 옳지 않다.
자기의 시나리오 상 의도된 Exception이라면 ERROR 레벨로 작성할 이유가 없다. Exception은 '예외' 이기 때문에 의도한 경우니깐 INFO로 적는 것이 더 좋다고 한다.
파일 업로드시 고민
- 동일한 이름으로 파일 올리면 기존 파일 삭제
- 이미지 파일/ 일반 파일 구분
- 첨부파일 공격 대비(exe,sh,zip의 파일 형식은 막는다 바이러스 프로그램이 있을 수 있기 때문에..)
- 이미지 파일 경우 원본 파일 용량이 큰 경우 섬네일 이미지 생성해야함
이 문제를 어디서 처리하느냐? JS에서 처리한다.
var regex = new RegExp("(.*?)\.(exe|sh|zip|alz)$");
var maxSize = 5242880; // 5MB
function checkExtension(fileName, fileSize){
if(fileSize >= maxSize){
alert("파일 사이즈 초과");
return false;
}
if(regex.test(fileName)){
alert("해당 종류의 파일은 업로드 할 수 없습니다.");
return false;
}
return true;
}
$(document).ready(function(){
$("#uploadBtn").on("click",function(e){
var formData = new FormData();
var inputFile = $("input[name='uploadFile']");
var files = inputFile[0].files;
console.log(files);
//add filedate to formdata
for(var i=0; i<files.length; i++){
if(!checkExtension(files[i].name, files[i].size)){
return false;
}
formData.append("uploadFile", files[i]);
}
$.ajax({
url: '/uploadAjaxAction',
processData: false,
contentType: false,
data: formData,
type: 'POST',
success: function(result){
alert("Uploaded");
}
});
});
});
사이즈가 너무 크면 먼저 if문에서 걸러주고,
그 다음 확장자를 정규식으로 걸러준다.
중복된 이름의 파일은?
현재 시간을 포함한 파일 이름을 생성하거나, UUID를 이용해서 중복이 발생할 가능성이 거의 없는 문자열을 생성해서 처리해준다.
한 폴더에 너무 많은 파일이 있기 보다는 '년/월/일'로 묶어서 단위로 폴더를 만드는 방법이 좋다.
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 String getFolder() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date date = new Date();
String str = sdf.format(date);
return str.replace("-", File.separator);
}
@PostMapping("/uploadAjaxAction")
public void uploadAjaxPost(MultipartFile[] uploadFile) {
String uploadFolder = "D:\\upload";
File uploadPath = new File(uploadFolder, getFolder());
log.info("upload path : " + uploadPath);
if(uploadPath.exists() == false) {
uploadPath.mkdirs();
}
for(MultipartFile multipartFile : uploadFile) {
log.info("------------");
log.info("Upload File Name : " + multipartFile.getOriginalFilename());
log.info("Upload File Size : " + multipartFile.getSize());
String uploadFileName = multipartFile.getOriginalFilename();
uploadFileName = uploadFileName.substring(uploadFileName.lastIndexOf("\\")+1);
log.info("only file name : " + uploadFileName);
File saveFile = new File(uploadPath, uploadFileName);
try {
multipartFile.transferTo(saveFile);
} catch (Exception e) {
log.error(e.getMessage());
}
}
}
SimpleDateFormat 객체인 sdf를 생성해주고 년도 월 일을 받고 Date로 오늘의 날짜를 받는다.
그리고 replace로 "-"로 구분해서 폴더를 만들어준다.
여기서 mkdirs()는 한 번에 여러 디렉토리를 생성하고
mkdir()은 한 번에 하나의 디렉토리만 생성한다고 한다.
unloadPath.exists()를 해준 구문은 만약 해당 폴더가 없으면 해당 폴더를 만들어 주는 구문이 된다.
중복 방지 파일 이름이 동일한 이름이면?
UUID값을 이용해서 처리해준다.
UUID는 Universally Unique identifier의 약자 , 정보 식별을 위해 사용되는 식별자임
128-bit 숫자로 이루어져 있음.
UUID의 장점은 , 데이터들이 나중에 단일 DB로 통합되거나, 같은 채널에 전송되더라도 식별자가 중복될 확률이 매우 낮다라는 점
log.info("only file name : " + uploadFileName);
UUID uuid = UUID.randomUUID();
uploadFileName = uuid.toString() + "_" + uploadFileName;
File saveFile = new File(uploadPath, uploadFileName);
UUID객체를 생성해서, FileName에다가 앞에 식별자를 붙여주고 _를 한뒤 파일 네임을 뒤로 하면 된다.
원본 이름이 같더라도 다른 이름의 파일로 생성된다!
'Spring' 카테고리의 다른 글
스프링 : 댓글 에러 해결 (1) | 2021.11.09 |
---|---|
스프링 : 섬네일 이미지 생성 (0) | 2021.11.07 |
스프링 : AOP와 트랜잭션 (0) | 2021.11.02 |
스프링 : UriComponentsBuilder 클래스 (0) | 2021.10.28 |
스프링 : 오라클 인덱스 생성, consumes , produces ,responseEntitiy (0) | 2021.10.28 |