[Spring Boot] 파일 업로드와 섬네일 처리

2023. 10. 9. 22:11·웹 개발/Spring

주요 기능

 

  • 이미지 파일 업로드
  • 이미지 여러개 동시 업로드
  • 날짜 폴더 생성
  • 섬네일 처리
  • 이미지 삭제 처리

나중에 구현할 기능

 

  • 모든 형식 파일 업로드 가능
  • 파일 정보를 DB에 저장

 

 

 


 

 

 

application.yml

org:
  zerock:
      upload:
        path: "C:\\upload"
  
spring:
  servlet:
    multipart:
      enabled: 'true'
      max-request-size: 30MB
      max-file-size: 10MB
      location: C:\upload

yml 파일에서 멀티파트의 (파일)데이터 처리 관련 속성 중, 파일 업로드 가능 여부를 true, 멀티파트 요청에 대한 최대 크기 제한, 멀티파트 요청 내의 개별 파일에 대한 최대 크기 제한, 업로드 된 파일이 서버의 파일 시스템에 저장될 위치를 지정한다.

 

 

참고로 아래의 yml과 properties 파일을 서로 변환시켜주는 사이트가 아주 유용하다.

Yaml Converter - MAGEDDO

 

 

 

 

UploadTestController

@Controller
public class UploadTestController {

	@GetMapping("/uploadEx")
	public void uploadEx() {
	}
}

 
 
 
 
 

UploadController

@RestController
@Log4j2
public class UploadController {

	
	@Value("${org.zerock.upload.path}")
	private String uploadPath;
	
	@PostMapping("/uploadAjax")
	public ResponseEntity<List<UploadResultDTO>> uploadFile(MultipartFile[] uploadFiles) throws IOException{
		
		List<UploadResultDTO> resultDTOList = new ArrayList<>();
		
		for(MultipartFile uploadFile : uploadFiles) {
			
			//이미지 파일만 업로드 가능
			if(uploadFile.getContentType().startsWith("image") == false) {
				log.warn("this file is not image type");
				return new ResponseEntity<>(HttpStatus.FORBIDDEN);
			}
			
			//실제 파일 이름 IE나 Edge나 전체 경로가 들어오므로
			String originalName = uploadFile.getOriginalFilename();
			String fileName = originalName.substring(originalName.lastIndexOf("\\") + 1);
			
			log.info("fileName : " + fileName);
			
			//날짜 폴더 생성
			String folderPath = makeFolder();
			
			String uuid = UUID.randomUUID().toString();
			
			//저장할 파일 이름 중간에 "_"를 사용해 구분
			String saveName = uploadPath + File.separator + folderPath +
					File.separator + uuid + "_" + fileName;
			
			Path savePath = Paths.get(saveName);
			
			try {
				uploadFile.transferTo(savePath);	//원본 이미지 저장
				
				//섬네일 생성
				String thumbnailSaveName = uploadPath + File.separator + folderPath + File.separator
						 + "s_" + uuid + "_" + fileName;
				//섬네일 파일 이름은 s_로 시작하도록함
				
				File thumbnailFile = new File(thumbnailSaveName);
				//섬네일 생성
				Thumbnailator.createThumbnail(savePath.toFile(), thumbnailFile, 100, 100);
				
				resultDTOList.add(new UploadResultDTO(fileName, uuid, folderPath));
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return new ResponseEntity<>(resultDTOList, HttpStatus.OK);
	}

	private String makeFolder() {
		
		String str = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
		
		String folderPath = str.replace("/", File.separator);
		
		//make folder
		File uploadPathFolder = new File(uploadPath, folderPath);
		
		if(uploadPathFolder.exists() == false) {
			uploadPathFolder.mkdirs();
		}
		return folderPath;
	}
	
	
	@GetMapping("/display")
	public ResponseEntity<byte[]> getFile(String fileName) {
		
		ResponseEntity<byte[]> result = null;
		
		try {
			String srcFileName = URLDecoder.decode(fileName, "UTF-8");
			
			log.info("fileName : " + srcFileName);
			
			File file = new File(uploadPath + File.separator + srcFileName);
			
			log.info("file : " + file);

			HttpHeaders header = new HttpHeaders();
			
			//MIME 타입 처리
			header.add("Content-Type", Files.probeContentType(file.toPath()));
			//파일 데이터 처리
			result = new ResponseEntity<>(FileCopyUtils.copyToByteArray(file),
					header, HttpStatus.OK);
			
		} catch (Exception e) {
			log.error(e.getMessage());
			return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
		}
		return result;
	}
	
	
	//업로드 파일 삭제
	@PostMapping("/removeFile")
	public ResponseEntity<Boolean> removeFiles(String fileName) {
		
		String srcFileName = null;
		
		try {
			srcFileName = URLDecoder.decode(fileName, "UTF-8");
			File file = new File(uploadPath + File.separator + srcFileName);
			boolean result = file.delete();
			
			File thumbnail = new File(file.getParent(), "s_" + file.getName());
			
			result = thumbnail.delete();
			
			return new ResponseEntity<>(result, HttpStatus.OK);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return new ResponseEntity<>(false, HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}
}

 
 
 
 
 

UploadResultDTO

@Data
@AllArgsConstructor
public class UploadResultDTO implements Serializable {

	private String fileName;
	private String uuid;
	private String folderPath;
	
	public String getImageURL() {
		
		try {
			return URLEncoder.encode(folderPath + "/" + uuid + "_" + fileName, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return "";
	}
	
	public String getThumbnailURL() {
		try {
			return URLEncoder.encode(folderPath + "/s_" + uuid + "_" + fileName, "UTF-8"); 
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return "";
	}
	
}

 
 
 
 

uploadEx.html

<!DOCTYPE html>
<html leng="en" xmlns="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<input name="uploadFiles" type="file" multiple>
	<button class="uploadBtn">Upload</button>
	
	<div class="uploadResult">
	</div>
	
	<script 
			src="https://code.jquery.com/jquery-3.5.1.min.js"
			integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
			crossorigin="anonymous"></script>
	
	<script>
	
		$('.uploadBtn').click(function( ) {
			
			var formData = new FormData();
			var inputFile = $("input[type='file']");
			var files = inputFile[0].files;
			
			for(var i = 0; i < files.length; i++) {
				console.log(files[i]);
				formData.append("uploadFiles", files[i]);
			}
		
			function showUploadedImages(arr) {
				
				console.log(arr);
				
				var divArea = $(".uploadResult");
				var str = "";
				
				for(var i = 0; i < arr.length; i++) {
					str += "<div>";
					str += "<img src = '/display?fileName=" + arr[i].thumbnailURL+"'>";
					str += "<button class='removeBtn' data-name='"+arr[i].imageURL +"'>REMOVE</button>"
					str += "</div>";
					divArea.append(str);
				}
			}
			
			//실제 업로드 부분
			$.ajax({
				url: '/uploadAjax',
				processData: false,
				contentType: false,
				data: formData,
				type: 'POST',
				dataType: 'json',
				success: function(result) {
					showUploadedImages(result);
					//나중에 화면 처리
				},
				error: function(jqXHR, textStatus, errorThrown) {
					console.log(textStatus);
				}
			});
			
			$(".uploadResult").on("click", ".removeBtn", function(e) {
				
				var target = $(this);
				var fileName = target.data("name");
				var targetDiv = $(this).closest("div");
				
				console.log(fileName);
				
				$.post('/removeFile', {fileName: fileName}, function(result) {
					console.log(result);
					if(result == true) {
						targetDiv.remove();
					}
				})
			});
			
		});
	
	</script>
</body>
</html>

 
 


 
 
실행 화면 - 포트 번호 : 82

 
전용 날짜 폴더를 생성하고, UUID로 이름이 중복되지 않게 저장한다.
섬네일 처리가 되어 크기가 100X100으로 맞춰진 상태이며 REMOVE 버튼 클릭 시 삭제된다.
 
 
 

반응형

'웹 개발 > Spring' 카테고리의 다른 글

Chat GPT로 사이트 UI를 구현해보자 - GPT 웹페이지 구현 100% 활용법  (0) 2024.11.20
Spring 컨트롤러 이름 변경 후 org.springframework.beans.factory.BeanCreationException 에러 해결 방법  (0) 2024.11.19
Spring Boot 프로젝트 생성하기 - 이클립스(Eclipse), 인텔리제이(Intellij)  (0) 2024.11.13
[Spring Boot] Tmap API를 이용한 Geocoding - 위치 정보를 좌표로 반환하기  (0) 2024.01.29
[Spring Boot] CoolSMS API를 이용한 휴대폰 본인인증  (0) 2023.10.09
'웹 개발/Spring' 카테고리의 다른 글
  • Spring 컨트롤러 이름 변경 후 org.springframework.beans.factory.BeanCreationException 에러 해결 방법
  • Spring Boot 프로젝트 생성하기 - 이클립스(Eclipse), 인텔리제이(Intellij)
  • [Spring Boot] Tmap API를 이용한 Geocoding - 위치 정보를 좌표로 반환하기
  • [Spring Boot] CoolSMS API를 이용한 휴대폰 본인인증
오은이
오은이
  • 오은이
    오은이 하우스
    오은이
  • 전체
    오늘
    어제
    • 분류 전체보기 (85)
      • 일기 (2)
      • Python (1)
      • Java (4)
      • CS (2)
      • 코딩테스트 (26)
        • 백준 (25)
        • 프로그래머스 (1)
      • 웹 개발 (18)
        • Spring (7)
        • JavaScript (3)
        • WebSquare (5)
        • React (3)
      • DB (5)
        • MySQL (4)
        • Oracle (1)
      • 서버&인프라 (18)
        • Server (5)
        • Cloud (12)
        • Linux (1)
      • 자격증 (9)
        • 정보처리기사 (2)
        • AICE (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • GitHub
  • 공지사항

  • 인기 글

  • 태그

    톰캣
    자바
    MySQL
    백준
    SpringBoot
    오블완
    cloud DB
    Associate
    db
    docker
    클라우드 배포
    docker배포
    머신러닝
    AI
    react
    알고리즘
    Java
    Apache
    dockerspring
    websquare
    백준자바
    티스토리챌린지
    리액트
    tomcat
    Spring
    웹스퀘어
    클라우드
    AICE
    EC2
    AICE Associate
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
오은이
[Spring Boot] 파일 업로드와 섬네일 처리
상단으로

티스토리툴바