Chapter 22-1

ChangWoo·2023년 11월 9일
0
post-thumbnail

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가 생성된 파일이 생기므로, 원본 이름과 같더라도 다른 이름의 파일로 생성되는 것을 확인할 수 있다.
profile
한 걸음 한 걸음 나아가는 개발자

0개의 댓글