romworld

Spring 10 - LPROD를 이용한 파일 업로드, 썸네일 본문

Spring

Spring 10 - LPROD를 이용한 파일 업로드, 썸네일

inderrom 2023. 1. 27. 17:41
1) Common0-fileupload 라이브러리
2) MultipartFile 를 사용하여
파일 업로드 구현을 해보자!

 

 


파일 업로드 환경설정

 

먼저 mvn repository에 가서 maven 코드 복사

 

  • javax.servlet-api 3.1.0
  • commons-fileupload 1.4
  • commons-io 2.11.0
  • (썸네일) imgscalr-lib 4.2
  • (썸네일) thumbnailator 0.4.8

 

 

maven 프로젝트에 매핑해주자.

 

<pom.xml> 

		<!-- 파일업로드 시작 -->
		<!-- commons -fileuplaod 라이브러리는 tomcat7.0버전 이후로는
			서블릿3.0이상에서 지원함
		-->
		<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
		<dependency>
		    <groupId>javax.servlet</groupId>
		    <artifactId>javax.servlet-api</artifactId>
		    <version>3.1.0</version>
		    <scope>provided</scope>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
		<dependency>
		    <groupId>commons-fileupload</groupId>
		    <artifactId>commons-fileupload</artifactId>
		    <version>1.4</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
		<dependency>
		    <groupId>commons-io</groupId>
		    <artifactId>commons-io</artifactId>
		    <version>2.11.0</version>
		</dependency>
		
		<!-- 썸네일 -->
		<!-- https://mvnrepository.com/artifact/org.imgscalr/imgscalr-lib -->
		<dependency>
		    <groupId>org.imgscalr</groupId>
		    <artifactId>imgscalr-lib</artifactId>
		    <version>4.2</version>
		</dependency>
		
		<!-- https://mvnrepository.com/artifact/net.coobird/thumbnailator -->
		<dependency>
		    <groupId>net.coobird</groupId>
		    <artifactId>thumbnailator</artifactId>
		    <version>0.4.8</version>
		</dependency>
		<!-- 파일업로드 끝 -->

매핑 후 프로젝트 우클릭 - Run as - maven build 해준다.

 


<root-context.xml>

	 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
	 	<!-- 파일 업로드 용량(10MB) -->
	 	<property name="maxUploadSize" value="10485760" />
	 	<property name="defaultEncoding" value="UTF-8" />
	 </bean>
	 
	 <!-- 파일업로드 디렉토리 설정 -->
	 <bean id="uploadPath" class="java.lang.String">
	 	<constructor-arg value="c:\\upload" />
	 </bean>

 

<web.xml>

1. servlet 태그 안에다가

 

	<!-- web.xml의 설정은 tomcat 자체 설정 -->
		<multipart-config>
			<location>c:\\upload</location><!-- 어디에 업로드? -->
			<max-file-size>20971520</max-file-size><!-- 업로드 최대크기 1MB * 20 -->
			<max-request-size>4194340</max-request-size><!-- 한번에 업로드 크기 40MB -->
			<file-size-threshold>20971520</file-size-threshold><!-- 메모리 20MB -->
		</multipart-config>

multipart-config를 추가해준다.

 


	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
		
		<!-- web.xml의 설정은 tomcat 자체 설정 -->
		<multipart-config>
			<location>c:\\upload</location><!-- 어디에 업로드? -->
			<max-file-size>20971520</max-file-size><!-- 업로드 최대크기 1MB * 20 -->
			<max-request-size>4194340</max-request-size><!-- 한번에 업로드 크기 40MB -->
			<file-size-threshold>20971520</file-size-threshold><!-- 메모리 20MB -->
		</multipart-config>
		
	</servlet>

 

2.  servlet - mapping 태그 밑에  multipart 필터 추가

<!-- mlultipart filter 추가 -->
	<filter>
		<display-name>springMultipartFilter</display-name>
		<filter-name>springMultipartFilter</filter-name>
		<filter-class>org.springframework.web.multipart.support.MultipartFilter</filter-class>
	</filter>
	
	<filter-mapping>
		<filter-name>springMultipartFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

 


servers의

<context.xml>

<!-- 파싱: 구문분석, 의미분석 -->
<Context allowCasualMultipartParsing="true">
	<!-- 캐시문제 해결 -->
	<Resources cashingAllowed="true" cacheMaxSize="100000"></Resources>

 

 


파일 업로드를 위한 jsp 파일 생성

 

<LprodController.java>

	//요청URL : /lprod/uploadForm
	//요청방식 : get
	@GetMapping("/uploadForm")
	public String uploadForm() {
		//forwading
		return "lprod/uploadForm";
	}

- 우선 컨트롤러에서 jsp로 요청 보낸다

<uploadForm.jsp>

<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- 파일업로드
1) method는 꼭 post!
2) enctype="multipart/form-data"
3) <input type="file" name="uploadFile" .. name 속성이 꼭 있어야 함!
 -->
 
 <!-- 
 요청URL : /lprod/uploadFormAction
 요청파라미터 : uploadFile이라는 이름의 파일객체
 요청방식 : post
  -->
<form action="/lprod/uploadFormAction" method="post"
	enctype="multipart/form-data">
	<input type="file" name="uploadFile" />
	<button type="submit">업로드</button>
</form>

<!-- 롬아 사랑해 -->

 

<LprodController.java>

/*
	  요청URL : /lprod/uploadFormAction
 	  요청파라미터 : uploadFile이라는 이름의 파일객체
   	  요청방식 : post 
	 */
	@PostMapping("/uploadFormAction")
	public String uploadFormAction(MultipartFile uploadFile) {
		//MultipartFile : 스프링에서 제공해주는 타입
		/*
		  String getOriginalFileName() : 업로드 되는 파일의 실제 파일명
		  boolean, isEmpty() : 파일이 없다면 true
		  long, getSize() : 업로드되는 파일의 크기
		  transferTo(File file) : 파일을 저장 
		 */
		
		// 파일이 저장되는 경로
		String uploadFolder = "c:\\upload";
		
		log.info("-------------");
		log.info("이미지명 : "+ uploadFile.getOriginalFilename());
		log.info("파일 크기 : "+ uploadFile.getSize());
		// 파라미터가 2개가 필요하다 (어디에, 무엇을)
		// 계획을 세움
		File saveFile = new File(uploadFolder,uploadFile.getOriginalFilename());
		// 계획 실행. 파일이 복사됨(클아이언트의 파일을 서버의 공간으로 복사)
		try {
			uploadFile.transferTo(saveFile);
		} catch (IllegalStateException e) {
			log.error(e.getMessage());
		} catch (IOException e) {
			log.error(e.getMessage());
		}
		return "redirect:/lprod/uploadForm";
	}

재기동 후 실행 시키자!

 

업로드 해보면
콘솔에 설정한 로그가 찍히고
지정한 경로 폴더에 이미지가 업로드 된다.


다중업로드

input 태그에 multiple 속성을 추가해주면 다중 업로드가 가능하다

 

<uploadForm.jsp>

<hr />
<form action="/lprod/uploadFormMultiAction" method="post"
	enctype="multipart/form-data">
	<input type="file" name="uploadFile" multiple />
	<button type="submit">다중업로드</button>
</form>

 

  • 다중업로드이므로 MultipartFile[] 배열을 사용한다.

<LprodControlelr.java>

@PostMapping("/uploadFormMultiAction")
	public String uploadFormMultiAction(MultipartFile[] uploadFile) {
		// 파일이 저장되는 경로
		String uploadFolder = "c:\\upload";
		
		for(MultipartFile multipartfile : uploadFile) {
			log.info("-------------");
			log.info("이미지명 : "+ multipartfile.getOriginalFilename());
			log.info("파일 크기 : "+ multipartfile.getSize());
			log.info("컨텐츠타입 :" + multipartfile.getContentType());
			
			File saveFile = new File(uploadFolder,multipartfile.getOriginalFilename());

			try {
				multipartfile.transferTo(saveFile);
			} catch (IllegalStateException e) {
				log.error(e.getMessage());
			} catch (IOException e) {
				log.error(e.getMessage());
			}
		}
		
		return "redirect:lprod/uploadForm";
	}

 

- 실행시키면 선택한 파일대로 파일 갯수가 보이고 마우스를 위에 올리면 파일명이 보인다.

log 출력됨
이미지도 잘 들어감

 


이미지 미리보기

원래 작성해놨던 업로드폼을 고쳐서
이미지가 미리 보일 수 있게 고쳐보자.

 

<uploadForm.jsp>

<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!-- 파일업로드
1) method는 꼭 post!
2) enctype="multipart/form-data"
3) <input type="file" name="uploadFile" .. name 속성이 꼭 있어야 함!
 -->
<script type="text/javascript" src="/resources/js/jquery.3.6.0.js"></script>
<script type="text/javascript">
$(function(){
	// - 이미지 미리보기 시작 -
	let sel_file = [];	
	$("#input_imgs").on("change", handleImgFileSelect);
	$("#input_imgs2").on("change", handleImgFileSelect);
	// 파라미터e : onchange 이벤트 객체
	function handleImgFileSelect(e){
		// 이벤트가 발생된  타겟 안에 들어있는 이미지 파일들을 가져옴
		let files = e.target.files;
		// 이미지가 여러개가 있을 수 있으므로 이미지들을 분리해서 배열로 만듦
		let fileArr = Array.prototype.slice.call(files);
		// 파일 오브젝트의 배열 반복. f : 배열 안에 들어있는 각각의 이미지 파일 객체
		fileArr.forEach(function(f){
			// 이미지 파일이 아닌 경우 이미지 미리보기 실패 처리(image/jpeg)
			if(!f.type.match("image.*")){
				alert("이미지 확장자만 가능합니다");
				//함수 종료
				return;
			}
			// 이미지 객체(f)를 전역 배열타입 변수(sel_file)에 넣자
			sel_file.push(f);
			// 이미지 객체를 읽을 자바스크립트의 reader 객체 생성
			let reader = new FileReader();
			// e : reader 객체가 이미지 객체를 읽는 이벤트
			reader.onload = function(e){
				// e.target : 이미지 객체
				// e.target.result : reader가 이미지를 다 읽은 결과
				let img_html = "<p><img src=\"" + e.target.result + "\"/></p>";
				// div 사이에 이미지가 렌더링되어 화면에 보임
				//객체.append : 누적, .html : 새로고침, innerHTML : J/S
				$(".imgs_wrap").append(img_html);
			}
			// f : 이미지 파일 객체를 읽은 후 다음 이미지 파일(f)를 위해 초기화 함
			reader.readAsDataURL(f);
			
		});//end forEach
	}
	// - 이미지 미리보기 끝 -
});
</script>
	 <!-- 
 요청URL : /lprod/uploadFormAction
 요청파라미터 : uploadFile이라는 이름의 파일객체
 요청방식 : post
  -->
<div class="imgs_wrap"></div>
<form action="/lprod/uploadFormAction" method="post"
	enctype="multipart/form-data">
	<input type="file" id="input_imgs" name="uploadFile" />
	<button type="submit">업로드</button>
</form>
<hr />
<form action="/lprod/uploadFormMultiAction" method="post"
	enctype="multipart/form-data">
	<input type="file" id="input_imgs2" name="uploadFile" multiple />
	<button type="submit">다중업로드</button>
</form>

 


파일명 중복 방지

UUID 를 사용하여 파일명 중복 방지를 해보자 ~
// - 파일명 중복 방지 시작  -
		//java.util.UUID => 랜덤값을 생성
		UUID uuid = UUID.randomUUID();
		// ex) ERASDFASDFASD_새롬이.JSP
		uploadFileName = uuid.toString() + "_" + uploadFileName;
		// - 파일명 중복 방지 끝  -

 

<LprodController.java> 수정

/*
	  요청URL : /lprod/uploadFormAction
 	  요청파라미터 : uploadFile이라는 이름의 파일객체
   	  요청방식 : post 
	 */
	@PostMapping("/uploadFormAction")
	public String uploadFormAction(MultipartFile uploadFile) {
		//MultipartFile : 스프링에서 제공해주는 타입
		/*
		  String getOriginalFileName() : 업로드 되는 파일의 실제 파일명
		  boolean, isEmpty() : 파일이 없다면 true
		  long, getSize() : 업로드되는 파일의 크기
		  transferTo(File file) : 파일을 저장 
		 */
		
		// 파일이 저장되는 경로
		String uploadFolder = "c:\\upload";
		// 파일명
		String uploadFileName = uploadFile.getOriginalFilename();
		log.info("---------------");
		log.info("-------------");
		log.info("이미지명 : "+ uploadFileName);
		log.info("파일 크기 : "+ uploadFile.getSize());

		// - 파일명 중복 방지 시작  -
		//java.util.UUID => 랜덤값을 생성
		UUID uuid = UUID.randomUUID();
		// ex) ERASDFASDFASD_새롬이.JSP
		uploadFileName = uuid.toString() + "_" + uploadFileName;
		// - 파일명 중복 방지 끝  -
		
		// 계획을 세움
		// 파라미터가 2개가 필요하다 (어디에, 무엇을)
		File saveFile = new File(uploadFolder,uploadFileName);
		// 계획 실행. 파일이 복사됨(클아이언트의 파일을 서버의 공간으로 복사)
		try {
			uploadFile.transferTo(saveFile);
		} catch (IllegalStateException e) {
			log.error(e.getMessage());
		} catch (IOException e) {
			log.error(e.getMessage());
		}
		return "redirect:/lprod/uploadForm";
	}

 


폴더명을 날짜 형식으로 생성

		// 파일이 저장되는 경로
		String uploadFolder = "c:\\upload";
		
		// - make folder 시작 -
		//2023-01-27 형식(format) 지정
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		
		// 날짜 객체 생성(java.util 패키지)
		Date date = new Date();
		//2023-01-27
		String str = sdf.format(date);
		// 단순 날짜 문자를 File객체의 폴더 타입으로 바꾸기
		// c:\\upload\\2023\\01\\27
		File uploadPath = new File(uploadFolder, str.replace("-", File.separator));
		log.info("upload Path : " + uploadPath);
		
		//만약 연/월/일 해당 폴더가 없다면 생성
		if(uploadPath.exists()==false) {
			uploadPath.mkdirs();
		}
		// - make folder 끝 -

 

<LprodController.java>

	/*
	  요청URL : /lprod/uploadFormAction
 	  요청파라미터 : uploadFile이라는 이름의 파일객체
   	  요청방식 : post 
	 */
	@PostMapping("/uploadFormAction")
	public String uploadFormAction(MultipartFile uploadFile) {
		//MultipartFile : 스프링에서 제공해주는 타입
		/*
		  String getOriginalFileName() : 업로드 되는 파일의 실제 파일명
		  boolean, isEmpty() : 파일이 없다면 true
		  long, getSize() : 업로드되는 파일의 크기
		  transferTo(File file) : 파일을 저장 
		 */
		
		// 파일이 저장되는 경로
		String uploadFolder = "c:\\upload";
		
		// - make folder 시작 -
		//2023-01-27 형식(format) 지정
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		
		// 날짜 객체 생성(java.util 패키지)
		Date date = new Date();
		//2023-01-27
		String str = sdf.format(date);
		// 단순 날짜 문자를 File객체의 폴더 타입으로 바꾸기
		// c:\\upload\\2023\\01\\27
		File uploadPath = new File(uploadFolder, str.replace("-", File.separator));
		log.info("upload Path : " + uploadPath);
		
		//만약 연/월/일 해당 폴더가 없다면 생성
		if(uploadPath.exists()==false) {
			uploadPath.mkdirs();
		}
		// - make folder 끝 -
		
		// 파일명
		String uploadFileName = uploadFile.getOriginalFilename();
		log.info("---------------");
		log.info("-------------");
		log.info("이미지명 : "+ uploadFileName);
		log.info("파일 크기 : "+ uploadFile.getSize());

		// - 파일명 중복 방지 시작  -
		//java.util.UUID => 랜덤값을 생성
		UUID uuid = UUID.randomUUID();
		// ex) ERASDFASDFASD_새롬이.JSP
		uploadFileName = uuid.toString() + "_" + uploadFileName;
		// - 파일명 중복 방지 끝  -
		
		// 계획을 세움
		// 파라미터가 2개가 필요하다 (어디에, 무엇을)
		File saveFile = new File(uploadPath,uploadFileName);
		// 계획 실행. 파일이 복사됨(클아이언트의 파일을 서버의 공간으로 복사)
		try {
			uploadFile.transferTo(saveFile);
		} catch (IllegalStateException e) {
			log.error(e.getMessage());
		} catch (IOException e) {
			log.error(e.getMessage());
		}
		return "redirect:/lprod/uploadForm";
	}

년/월/일로 폴더가 생기고 그 안에 파일이 업로드 된다.

 


다중 파일 업로드도 적용해주고

겹치는 소스를 메소드로 묶어서 간결하게 만들어보자

 

 

//연/월/일 폴더 생성
	public static String getFolder() {
		
				//2023-01-27 형식(format) 지정
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
				
				// 날짜 객체 생성(java.util 패키지)
				Date date = new Date();
				//2023-01-27 => 2023\\01\\27
				String str = sdf.format(date);
				// 단순 날짜 문자를 File객체의 폴더 타입으로 바꾸기
				// c:\\upload\\2023\\01\\27
				return str.replace("-", File.separator);
	}

<LprodControoler.java>

/*
	  요청URL : /lprod/uploadFormAction
 	  요청파라미터 : uploadFile이라는 이름의 파일객체
   	  요청방식 : post 
	 */
	@PostMapping("/uploadFormAction")
	public String uploadFormAction(MultipartFile uploadFile) {
		//MultipartFile : 스프링에서 제공해주는 타입
		/*
		  String getOriginalFileName() : 업로드 되는 파일의 실제 파일명
		  boolean, isEmpty() : 파일이 없다면 true
		  long, getSize() : 업로드되는 파일의 크기
		  transferTo(File file) : 파일을 저장 
		 */
		
		// 파일이 저장되는 경로
		String uploadFolder = "c:\\upload";
		
		// - make folder 시작 -
		// c:\\upload\\2023\\01\\27
		File uploadPath = new File(uploadFolder,getFolder());
		log.info("upload Path : " + uploadPath);
		
		//만약 연/월/일 해당 폴더가 없다면 생성
		if(uploadPath.exists()==false) {
			uploadPath.mkdirs();
		}
		// - make folder 끝 -
		
		// 파일명
		String uploadFileName = uploadFile.getOriginalFilename();
		log.info("---------------");
		log.info("-------------");
		log.info("이미지명 : "+ uploadFileName);
		log.info("파일 크기 : "+ uploadFile.getSize());

		// - 파일명 중복 방지 시작  -
		//java.util.UUID => 랜덤값을 생성
		UUID uuid = UUID.randomUUID();
		// ex) ERASDFASDFASD_새롬이.JSP
		uploadFileName = uuid.toString() + "_" + uploadFileName;
		// - 파일명 중복 방지 끝  -
		
		// 계획을 세움
		// 파라미터가 2개가 필요하다 (어디에, 무엇을)
		File saveFile = new File(uploadPath,uploadFileName);
		// 계획 실행. 파일이 복사됨(클아이언트의 파일을 서버의 공간으로 복사)
		try {
			uploadFile.transferTo(saveFile);
		} catch (IllegalStateException e) {
			log.error(e.getMessage());
		} catch (IOException e) {
			log.error(e.getMessage());
		}
		return "redirect:/lprod/uploadForm";
	}
	
	@PostMapping("/uploadFormMultiAction")
	public String uploadFormMultiAction(MultipartFile[] uploadFile) {
		// 파일이 저장되는 경로
		String uploadFolder = "c:\\upload";
		
		// - make folder 시작 -
		// c:\\upload\\2023\\01\\27
		File uploadPath = new File(uploadFolder,getFolder());
		log.info("upload Path : " + uploadPath);
				
		//만약 연/월/일 해당 폴더가 없다면 생성
		if(uploadPath.exists()==false) {
			uploadPath.mkdirs();
		}
		// - make folder 끝 -
		
		for(MultipartFile multipartfile : uploadFile) {
			// 파일명
			String uploadFileName = multipartfile.getOriginalFilename();
			log.info("-------------");
			log.info("이미지명 : "+ uploadFileName);
			log.info("파일 크기 : "+ multipartfile.getSize());
			log.info("컨텐츠타입 :" + multipartfile.getContentType());
			
			// - 파일명 중복 방지 시작  -
			//java.util.UUID => 랜덤값을 생성
			UUID uuid = UUID.randomUUID();
			// ex) ERASDFASDFASD_새롬이.JSP
			uploadFileName = uuid.toString() + "_" + uploadFileName;
			// - 파일명 중복 방지 끝  -
			
			//c:\\upload\\2023\\01\27\\ERASDFASDFASD_새롬이.jsp
			File saveFile = new File(uploadPath,uploadFileName);

			try {
				multipartfile.transferTo(saveFile);
			} catch (IllegalStateException e) {
				log.error(e.getMessage());
			} catch (IOException e) {
				log.error(e.getMessage());
			}
		}
		
		return "redirect:/lprod/uploadForm";
	}
	
	//연/월/일 폴더 생성
	public static String getFolder() {
		
				//2023-01-27 형식(format) 지정
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
				
				// 날짜 객체 생성(java.util 패키지)
				Date date = new Date();
				//2023-01-27 => 2023\\01\\27
				String str = sdf.format(date);
				// 단순 날짜 문자를 File객체의 폴더 타입으로 바꾸기
				// c:\\upload\\2023\\01\\27
				return str.replace("-", File.separator);
	}

다중 선택도 잘 들어간다.


썸네일

 

업로드 메서드 안에
try-catch문에서 작성해보자

 

 

<LprodController.java>

try {
			uploadFile.transferTo(saveFile);
			
			// 이미지인지 체킹
			try {
				//MIME 타입을 가져옴. image/jpeg
				String contentType = Files.probeContentType(saveFile.toPath());
				log.info("contentType : " + contentType);
				//MIME 타입 정보가 images로 시작하는지 여부 (true/false)
				if(contentType.startsWith("image")) { // 이미지가 맞다면 true
					//c:\\upload\\2023\\01\\27\\s_이지미명.jpg
					FileOutputStream thumbnail = new FileOutputStream(
							new File(uploadPath,"s_" + uploadFileName)
						);
					//섬네일 생성
					Thumbnailator.createThumbnail(uploadFile.getInputStream(),thumbnail,100,100);
					thumbnail.close();
				}
			}catch(IOException e) {
				e.printStackTrace();
			}
			
		} catch (IllegalStateException e) {
			log.error(e.getMessage());
		} catch (IOException e) {
			log.error(e.getMessage());
		}

 

 

다중업로드도 적용시켜보자

<LprodController.java>

	/*
	  요청URL : /lprod/uploadFormAction
 	  요청파라미터 : uploadFile이라는 이름의 파일객체
   	  요청방식 : post 
	 */
	@PostMapping("/uploadFormAction")
	public String uploadFormAction(MultipartFile uploadFile) {
		//MultipartFile : 스프링에서 제공해주는 타입
		/*
		  String getOriginalFileName() : 업로드 되는 파일의 실제 파일명
		  boolean, isEmpty() : 파일이 없다면 true
		  long, getSize() : 업로드되는 파일의 크기
		  transferTo(File file) : 파일을 저장 
		 */
		
		// 파일이 저장되는 경로
		String uploadFolder = "c:\\upload";
		
		// - make folder 시작 -
		// c:\\upload\\2023\\01\\27
		File uploadPath = new File(uploadFolder,getFolder());
		log.info("upload Path : " + uploadPath);
		
		//만약 연/월/일 해당 폴더가 없다면 생성
		if(uploadPath.exists()==false) {
			uploadPath.mkdirs();
		}
		// - make folder 끝 -
		
		// 파일명
		String uploadFileName = uploadFile.getOriginalFilename();
		log.info("---------------");
		log.info("-------------");
		log.info("이미지명 : "+ uploadFileName);
		log.info("파일 크기 : "+ uploadFile.getSize());

		// - 파일명 중복 방지 시작  -
		//java.util.UUID => 랜덤값을 생성
		UUID uuid = UUID.randomUUID();
		// ex) ERASDFASDFASD_새롬이.JSP
		uploadFileName = uuid.toString() + "_" + uploadFileName;
		// - 파일명 중복 방지 끝  -
		
		// 계획을 세움
		// 파라미터가 2개가 필요하다 (어디에, 무엇을)
		File saveFile = new File(uploadPath,uploadFileName);
		// 계획 실행. 파일이 복사됨(클아이언트의 파일을 서버의 공간으로 복사)
		try {
			uploadFile.transferTo(saveFile);
			
			// 이미지인지 체킹
			try {
				//MIME 타입을 가져옴. image/jpeg
				String contentType = Files.probeContentType(saveFile.toPath());
				log.info("contentType : " + contentType);
				//MIME 타입 정보가 images로 시작하는지 여부 (true/false)
				if(contentType.startsWith("image")) { // 이미지가 맞다면 true
					//c:\\upload\\2023\\01\\27\\s_이지미명.jpg
					FileOutputStream thumbnail = new FileOutputStream(
							new File(uploadPath,"s_" + uploadFileName)
						);
					//섬네일 생성
					Thumbnailator.createThumbnail(uploadFile.getInputStream(),thumbnail,100,100);
					thumbnail.close();
				}
			}catch(IOException e) {
				e.printStackTrace();
			}
			
		} catch (IllegalStateException e) {
			log.error(e.getMessage());
		} catch (IOException e) {
			log.error(e.getMessage());
		}
		return "redirect:/lprod/uploadForm";
	}
	
	@PostMapping("/uploadFormMultiAction")
	public String uploadFormMultiAction(MultipartFile[] uploadFile) {
		// 파일이 저장되는 경로
		String uploadFolder = "c:\\upload";
		
		// - make folder 시작 -
		// c:\\upload\\2023\\01\\27
		File uploadPath = new File(uploadFolder,getFolder());
		log.info("upload Path : " + uploadPath);
				
		//만약 연/월/일 해당 폴더가 없다면 생성
		if(uploadPath.exists()==false) {
			uploadPath.mkdirs();
		}
		// - make folder 끝 -
		
		for(MultipartFile multipartfile : uploadFile) {
			// 파일명
			String uploadFileName = multipartfile.getOriginalFilename();
			log.info("-------------");
			log.info("이미지명 : "+ uploadFileName);
			log.info("파일 크기 : "+ multipartfile.getSize());
			log.info("컨텐츠타입 :" + multipartfile.getContentType());
			
			// - 파일명 중복 방지 시작  -
			//java.util.UUID => 랜덤값을 생성
			UUID uuid = UUID.randomUUID();
			// ex) ERASDFASDFASD_새롬이.JSP
			uploadFileName = uuid.toString() + "_" + uploadFileName;
			// - 파일명 중복 방지 끝  -
			
			//c:\\upload\\2023\\01\27\\ERASDFASDFASD_새롬이.jsp
			File saveFile = new File(uploadPath,uploadFileName);

			try {
				multipartfile.transferTo(saveFile);
				
				// 이미지인지 체킹
				try {
					//MIME 타입을 가져옴. image/jpeg
					String contentType = Files.probeContentType(saveFile.toPath());
					log.info("contentType : " + contentType);
					//MIME 타입 정보가 images로 시작하는지 여부 (true/false)
					if(contentType.startsWith("image")) { // 이미지가 맞다면 true
						//c:\\upload\\2023\\01\\27\\s_이지미명.jpg
						FileOutputStream thumbnail = new FileOutputStream(
								new File(uploadPath,"s_" + uploadFileName)
							);
						//섬네일 생성
						Thumbnailator.createThumbnail(multipartfile.getInputStream(),thumbnail,100,100);
						thumbnail.close();
					}
				}catch(IOException e) {
					e.printStackTrace();
				}
			} catch (IllegalStateException e) {
				log.error(e.getMessage());
			} catch (IOException e) {
				log.error(e.getMessage());
			}
		}
		
		return "redirect:/lprod/uploadForm";
	}
	
	//연/월/일 폴더 생성
	public static String getFolder() {
		
				//2023-01-27 형식(format) 지정
				SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
				
				// 날짜 객체 생성(java.util 패키지)
				Date date = new Date();
				//2023-01-27 => 2023\\01\\27
				String str = sdf.format(date);
				// 단순 날짜 문자를 File객체의 폴더 타입으로 바꾸기
				// c:\\upload\\2023\\01\\27
				return str.replace("-", File.separator);
	}

 파일 업로드를 위한 ATTACH 테이블 생성 

 

SQL DEVELOPER를 킨다

 

- 테이블 생성

 


<AttachVO.java>

 

@Data
기본 생성자, getter/setter, toString을 자동으로 생성시키는 어노테이션
package kr.or.ddit.vo;

import java.util.Date;

import lombok.Data;

// 기본 생성자, getter/setter, toString를 생성시켜줌
// lombok의 힘을 빌림
// Pojo에 위반..
@Data
public class AttachVO {
	private int seq;
	private String filename;
	private int filesize;
	private String thumbnail;
	private Date regdate;
}

 

<INSERT문 SQL구문>

INSERT INTO ATTACH(SEQ, FILENAME, FILESIZE, THUMBNAIL, REGDATE)
VALUES(
    (SELECT NVL(MAX(SEQ),0)+1 FROM ATTACH),
    '/upload/2023/01/27//e4397843-a33f-4d00-8758-f17044f7efcc_KakaoTalk_20230127_145604043.jpg',
    12345,'s_e4397843-a33f-4d00-8758-f17044f7efcc_KakaoTalk_20230127_145604043.jpg',
    SYSDATE
);

 

 

<lprod_SQL.xml>

	<!-- 첨부파일 등록  -->
	<insert id="uploadFormAction" parameterType="attachVO">
		<selectKey resultType="int" order="BEFORE" keyProperty="seq" >
			SELECT NVL(MAX(SEQ),0)+1 FROM ATTACH
		</selectKey>
		INSERT INTO ATTACH(SEQ, FILENAME, FILESIZE, THUMBNAIL, REGDATE)
		VALUES(
		    #{seq},#{filename},#{filesize},#{thumbnail},SYSDATE
		)
	</insert>

 

 

<mybatisAlias.xml>

<typeAlias type="kr.or.ddit.vo.AttachVO" alias="attachVO" /> 추가
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
   <!-- 
   [마이바티스] 스프링에서 "_"를 사용한 컬럼명을 사용 시(BOOK 테이블의 BOOK_ID)
   카멜케이스로 읽어줌(bookId)
   ex) 테이블 컬럼명이 member_id인 경우 jsp화면단에서 이 값을 사용 시 memberId로 사용
   -->
   <settings>
      <setting name="mapUnderscoreToCamelCase" value="true"/>
   </settings>
   <!-- 자주 사용하는 타입의 별칭을 세팅 -->
   <typeAliases>
      <typeAlias type="kr.or.ddit.vo.BookVO" alias="bookVO" />      
      <typeAlias type="kr.or.ddit.vo.LprodVO" alias="LprodVO" />      
      <typeAlias type="kr.or.ddit.vo.AttachVO" alias="attachVO" />      
   </typeAliases>
</configuration>

 

<LprodDao.java>

	//첨부파일 등록 
	//<insert id="uploadFormAction" parameterType="attachVO">
	public int uploadFormAction(AttachVO attachVO) {
		return this.sqlSessionTemplate.insert("lprod.uploadFormAction",attachVO);
	}

 

 

<LprodService.java>

	// 첨푸파일 등록
	public int uploadFormAction(AttachVO attachVO);

 

<LprodServiceImpl.java>

	//첨부파일 등록
	@Override
	public int uploadFormAction(AttachVO attachVO) {
		return this.lprodDao.uploadFormAction(attachVO);
	}

 

<LprodController.java>

try-catch 부분을 수정해준다 (AttachVO 추가해줬기 때문에)
	/*
	  요청URL : /lprod/uploadFormAction
 	  요청파라미터 : uploadFile이라는 이름의 파일객체
   	  요청방식 : post 
	 */
	@PostMapping("/uploadFormAction")
	public String uploadFormAction(MultipartFile uploadFile) {
		//MultipartFile : 스프링에서 제공해주는 타입
		/*
		  String getOriginalFileName() : 업로드 되는 파일의 실제 파일명
		  boolean, isEmpty() : 파일이 없다면 true
		  long, getSize() : 업로드되는 파일의 크기
		  transferTo(File file) : 파일을 저장 
		 */
		
		// 파일이 저장되는 경로
		String uploadFolder = "c:\\upload";
		
		// - make folder 시작 -
		// c:\\upload\\2023\\01\\27
		File uploadPath = new File(uploadFolder,getFolder());
		log.info("upload Path : " + uploadPath);
		
		//만약 연/월/일 해당 폴더가 없다면 생성
		if(uploadPath.exists()==false) {
			uploadPath.mkdirs();
		}
		// - make folder 끝 -
		
		// 파일명
		String uploadFileName = uploadFile.getOriginalFilename();
		log.info("---------------");
		log.info("-------------");
		log.info("이미지명 : "+ uploadFileName);
		log.info("파일 크기 : "+ uploadFile.getSize());

		// - 파일명 중복 방지 시작  -
		//java.util.UUID => 랜덤값을 생성
		UUID uuid = UUID.randomUUID();
		// ex) ERASDFASDFASD_새롬이.JSP
		uploadFileName = uuid.toString() + "_" + uploadFileName;
		// - 파일명 중복 방지 끝  -
		
		// 계획을 세움
		// 파라미터가 2개가 필요하다 (어디에, 무엇을)
		File saveFile = new File(uploadPath,uploadFileName);
		// 계획 실행. 파일이 복사됨(클아이언트의 파일을 서버의 공간으로 복사)
		try {
			uploadFile.transferTo(saveFile);
			
			AttachVO attachVO = new AttachVO();
			//1) filename :  /2023/01/27/이지미명.jpg 
			String filename = "/" + getFolder().replace("\\", "/") + "/"+
							uploadFileName;
			attachVO.setFilename(filename);
			
			//2) filesize
			Long l = uploadFile.getSize();
			attachVO.setFilesize(l.intValue());
			
			//3) thumbnail
			String thumb = "/" + getFolder().replace("\\", "/") + "/s_"+
					uploadFileName;
			attachVO.setThumbnail(thumb);
			
			log.info("attachVO : " + attachVO);
			
			// 이미지인지 체킹
			try {
				//MIME 타입을 가져옴. image/jpeg
				String contentType = Files.probeContentType(saveFile.toPath());
				log.info("contentType : " + contentType);
				//MIME 타입 정보가 images로 시작하는지 여부 (true/false)
				if(contentType.startsWith("image")) { // 이미지가 맞다면 true
					//c:\\upload\\2023\\01\\27\\s_이지미명.jpg
					FileOutputStream thumbnail = new FileOutputStream(
							new File(uploadPath,"s_" + uploadFileName)
						);
					//섬네일 생성
					Thumbnailator.createThumbnail(uploadFile.getInputStream(),thumbnail,100,100);
					thumbnail.close();
				}
				//ATTACH 테이블에 insert
				int result = this.lprodService.uploadFormAction(attachVO);
				log.info("result : " + result);
			}catch(IOException e) {
				e.printStackTrace();
			}
			
		} catch (IllegalStateException e) {
			log.error(e.getMessage());
		} catch (IOException e) {
			log.error(e.getMessage());
		}
		return "redirect:/lprod/uploadForm";
	}

 

- 파일을 업로드하면

- 데이터베이스에 잘 insert되는 걸 볼 수 있다.


이제 마지막으로 강아지 이미지를

내 사진으로 바꿔보자 ㅋ

 

- 여기에서 file inputdml default , div class=""mb-3"를 카피해온다.

- 또한 부트스트랩 적용을 위해 추가해줌

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>

 

https://getbootstrap.com/docs/5.3/forms/form-control/

 

- (create.jsp)클래스 이름과 라벨을 지워준다. ( id, name) 추가 

<div class="form-group">
	 <input class="form-control" type="file" id="uploadFile" name="uploadFile">
</div>

- 스크립트 추가

$(function(){
	// - 이미지 미리보기 시작 -
	$("#input_imgs").on("change", handleImgFileSelect);
	
	function handleImgFileSelect(e){
		let files = e.target.files;
		let fileArr = Array.prototype.slice.call(files);
		fileArr.forEach(function(f){
			if(!f.type.match("image.*")){
				alert("이미지만 가능합니다.");
				return;
			}
			
			let reader = new FileReader();
			reader.onload = function(e){
				let img_html = "<img src=\"" + e.target.result + "\" />";
				$(".bg-register-image").html(img_html);
			}
			reader.readAsDataURL(f);
		});
	}
	// - 이미지 미리보기 끝 -

 

<create.jsp>

<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
<script type="text/javascript" src="/resources/js/jquery-3.6.0.js"></script>
<script>
// document의 요소들이 모두 로딩된 후 실행
$(function(){
	// - 이미지 미리보기 시작 -
	$("#input_imgs").on("change", handleImgFileSelect);
	
	function handleImgFileSelect(e){
		let files = e.target.files;
		let fileArr = Array.prototype.slice.call(files);
		fileArr.forEach(function(f){
			if(!f.type.match("image.*")){
				alert("이미지만 가능합니다.");
				return;
			}
			
			let reader = new FileReader();
			reader.onload = function(e){
				let img_html = "<img src=\"" + e.target.result + "\" style='width:100%;' />";
				$(".bg-register-image").html(img_html);
			}
			reader.readAsDataURL(f);
		});
	}
	// - 이미지 미리보기 끝 -
	
	
	$(".btn-icon-split").on("click", function(){
		console.log("새롬이");
		// 상품분류코드 자동생성
		/*
		url : 요청경로
		contentType : 보내는 데이터의 타입
		dataType : 응답 데이터 타입
		data : 요청 시 전송할 데이터
		type : 요청방식. get,post,put
		*/
		$.ajax({
			url:"/lprod/getLprodGu",
			type:"post",
			success:function(result){
				//console.log("result : " + result);
				$("input[name='lprodGu']").val(result);
			}
		});
	});
	
	
	$("#btnRegist").on("click", function(){
		let lprodGu = $("#lprodGu").val();
		let lprodNm = $("#lprodNm").val();
		
		if(jQuery.trim(lprodGu)==""){
			alert("상품분류코드를 작성해주세요");
			$("#lprodGu").focus();
			return false;
		}
		
		if(jQuery.trim(lprodNm)==""){
			$("#lprodNm").focus();
			alert("상품분류명을 작성해주세요");
		}
		
		// submit을 실행
		//$("#frm").submit();도 가능
		/*
		요청URI : /lprod/createPost
		요청파라미터 : {"lprodId:"10","lprodGu:"P404""lprodNm:"간식류"}
		요청방식 : post
		*/
		$("form").submit();
		
	});
	
});
</script>
<div class="container">

	<div class="card o-hidden border-0 shadow-lg my-5">
		<div class="card-body p-0">
			<!-- Nested Row within Card Body -->
			<div class="row">
				<div class="col-lg-5 d-none d-lg-block bg-register-image"></div>
				<div class="col-lg-7">
					<div class="p-5">
						<div class="text-center">
							<h1 class="h4 text-gray-900 mb-4">상품분류 추가!</h1>
						</div>
						<form id="frm" class="user" action="/lprod/createPost" 
							method="post" enctype="multipart/form-data">
							<div class="form-group row">
								<div class="col-sm-6 mb-3 mb-sm-0">
								<!-- 변경 제약
									disabled : 값이 전달 안됨
									readonly : 파라미터로 값이 전달됨 -->
									<input type="text" class="form-control form-control-user"
										id="lprodId" name="lprodId"
										value="${lprodId}"
										 placeholder="상품분류 아이디"
										 readonly>
								</div>
								<div class="col-sm-6">
									<a href="#" class="btn btn-info btn-icon-split">
                                        <span class="icon text-white-50">
                                            <i class="fas fa-info-circle"></i>
                                        </span>
                                        <span class="text">분류코드 자동생성</span>
                                    </a>
								</div>
							</div>
							<div class="form-group">
								<input type="email" class="form-control form-control-user"
									id="lprodGu" name="lprodGu" placeholder="상품분류 코드" 
									value="${data.lprodGu}" required />
							</div>
							<div class="form-group">
								<input type="text" class="form-control form-control-user"
									id="lprodNm" name="lprodNm" placeholder="상품분류 명" 
									value="${data.lprodNm}" required />
							</div>
							<div class="form-group">
								  <input class="form-control" type="file" id="input_imgs" name="uploadFile">
							</div>
							<a href="#" id="btnRegist" class="btn btn-primary btn-user btn-block">
								등록 </a>
							<hr>
							<a href="/lprod/list" class="btn btn-google btn-user btn-block">
								<i class="fab fa-google fa-fw"></i> 목록보기
							</a> 
						</form>
						<hr>
						<div class="text-center">
							<a class="small" href="forgot-password.html">Forgot Password?</a>
						</div>
						<div class="text-center">
							<a class="small" href="login.html">Already have an account?
								Login!</a>
						</div>
					</div>
				</div>
			</div>
		</div>
	</div>

</div>

 

 

<lprodVO>

lprodVO에 uploadFile 객체를 추가해준다. 그 후 getter/setter , toString 추가

 

<LprodController.java>

  • createPost 메서드를 수정해준다.
/*
	요청URI : /lprod/createPost
	요청파라미터 : {"lprodId:"10","lprodGu:"P404""lprodNm:"간식류"}
	요청방식 : post
	*/
	@PostMapping("/createPost")
	public String createPost(@ModelAttribute LprodVO lprodVO,
			Model model) {
		log.info("lprodVO :" +lprodVO);
		
		MultipartFile multipartFile = lprodVO.getUploadFile();
		log.info("multipartFile : " + multipartFile);
		
		int result = this.lprodService.createPost(lprodVO);
		
		if(result > 0) { //입력 성공
			return "redirect:/lprod/detail?lprodGu="+lprodVO.getLprodGu();
			
		}else { // 입력 실패
			model.addAttribute("data",lprodVO);
			model.addAttribute("lprodId",lprodVO.getLprodId());
			return "/lprod/create";
		}
		
	}

 

 

우선 실행시키면

강아지 위에 덮어씌워진다.

 

 

**다만 한글이 깨진다. 한글 처리를 바로 해보자**

Comments