Part 22. 파일 업로드 상세 처리
22.1 파일의 확장자나 크기의 사전 처리
- 최근 포털에서도 특정한 확장자를 제외한 파일들의 업로드를 제한하는 경우가 많은데, 이는 첨부파일을 이용하는 웹 공격을 막기 위해서 행해지는 조치다.
- 예제는 첨부파일의 확장자가 'exe, sh, zip'등의 경우에는 업로드를 제한하고, 특정 크기 이상의 파일은 업로드 할 수 없도록 제한하는 처리를 JavaScript로 처리한다.
- 파일 확장자의 경우 정규 표현식을 이용해 검사할 수 있다.
- uploadAjax.jsp에 파일의 확장자와 크기를 설정하고, 이를 검사하는 함수 checkExtension()을 작성해 적용하면 아래와 같은 형태가 된다.
< uploadAjax.jsp >
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;
}
$("#uploadBtn").on("click", function(e){
var formData = new FormData();
var inputFile = $("input[name='uploadFile']");
var files = inputFile[0].files;
console.log(files);
//add File Data 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");
}
}); //$.ajax
});
});
- 첨부파일을 업로드하면 for 루프에서 checkExtension()을 호출해 확장자와 파일의 크기를 체크하게 된다.
22.1.1 중복된 이름의 첨부파일 처리
- 첨부파일을 저장할 때 신경 쓰이는 것은 크게 두 가지로 1) 중복된 이름의 파일 처리와 2) 한 폴더 내에 너무 낳은 파일의 생성 문제다.
- 1)의 경우는 현재 시간을 밀리세컨드(천분의 1초단위)까지 구분해서 파일 이름을 생성해서 저장하거나 UUID를 이용해 중복이 발생할 가능성이 거의 없는 문자열을 생성해 처리한다.
- 2)의 경우는 하나의 폴더에 생성될 수 있는 파일의 개수에 대한 문제인데, 한 폴더에 너무 많은 파일이 있는 경우 속도의 저하와 개수의 제한 문제가 생기는 것을 방지해야 한다.
- 이에 대한 해결책으로 일반적인 방법은 '년/월/일' 단위의 폴더를 생성해 파일을 저장하는 것이다.
22.1.2 년/월/일 폴더의 생성
- 첨부파일을 보관하는 폴더를 생성하는 작업은 한 번에 폴더를 생성하거나 존재하는 폴더를 이용하는 방식을 사용한다.
- java.io.File에 존재하는 mkdirs()를 이용하면 필요한 상위 폴더까지 한 번에 생성할 수 있으므로 간단히 처리할 수 있다.
- UploadController에 추가적인 메서드와 수정을 통해 업로드 폴더 등을 처리하도록 한다.
< UploadController >
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) {
log.info("update ajax post.........");
String uploadFolder = "D:\\upload";
// make folder -------
// File saveFile = new File(uploadFolder, uploadFileName);
File saveFile = new File(uploadPath, uploadFileName);
log.info("upload path: " + uploadPath);
if(uploadPath.exists() == false) {
uploadPath.mkdirs();
}
// make yyyy/MM/dd folder
for (MultipartFile multipartFile : uploadFile) {
log.info("---------------------------------");
log.info("Upload File Name:" +multipartFile.getOriginalFilename());
log.info("Upload File Size:" +multipartFile.getSize());
String uploadFileName = multipartFile.getOriginalFilename();
// IE has file path
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());
} //end catch
} //end for
}
- getFolder()는 오늘 날짜의 경로를 문자열로 생성한다.
- 생성된 경로는 폴더 경로로 수정된 뒤에 반환한다.
- uploadAjaxPost()에서는 해당 경로가 있는지 검사하고, 폴더를 생성한다.
- 이후 생성된 폴더로 파일을 저장하게 한다.
- 위와 같이 폴더를 생성한 후 기존과 달리 uploadPath 경로에 파일을 저장하게 되면 자동으로 폴더가 생성되면서 파일이 저장되는 것을 볼 수 있다.
22.1.3 중복 방지를 위한 UUID 적용
- 파일 이름을 생성할 때 동일한 이름으로 업로드되면 기존 파일을 지우게 되므로 java.util.UUID의 값을 이용해 처리한다.
< UploadController >
@PostMapping("/uploadAjaxAction")
public void uploadAjaxPost(MultipartFile[] uploadFile) {
log.info("update ajax post.........");
String uploadFolder = "D:\\upload";
// make folder -------
File uploadPath = new File(uploadFolder, getFolder());
log.info("upload path: " + uploadPath);
if(uploadPath.exists() == false) {
uploadPath.mkdirs();
}
// make yyyy/MM/dd folder
for (MultipartFile multipartFile : uploadFile) {
log.info("---------------------------------");
log.info("Upload File Name:" +multipartFile.getOriginalFilename());
log.info("Upload File Size:" +multipartFile.getSize());
String uploadFileName = multipartFile.getOriginalFilename();
// IE has file path
uploadFileName = uploadFileName.substring(uploadFileName.lastIndexOf("\\") + 1);
log.info("only File name : " + uploadFileName);
UUID uuid = UUID.randomUUID();
uploadFileName = uuid.toString() + "_" + uploadFileName;
// File saveFile = new File(uploadFolder, uploadFileName);
File saveFile = new File(uploadPath, uploadFileName);
try {
multipartFile.transferTo(saveFile);
} catch (Exception e) {
log.error(e.getMessage());
} //end catch
} //end for
}
- 첨부파일은 randomUUID()를 이용해 임의의 값을 생성한다. 생성된 값은 원래의 파일 이름과 구분할 수 있도록 중간에'_'를 추가한다.
- 나중에 앞에서부터 '_'를 기준으로 분리하면 원래의 파일 이름을 파악할 수 있다.
- 이제 첨부파일을 업로드하면 UUID가 생성된 파일이 생기므로, 원본 이름과 같더라도 다른 이름의 파일로 생성되는 것을 확인할 수 있다.