반응형

 

파일 업로드 폼 방식 요청 처리

 

: 파일업로드 폼 파일 요소값을 스프링MVC가 지원하는 MultipartFile 매개변수로 처리함 

 

 

 

  • 기본 형태

MemberController.java

하단에 추가

/* 8. 파일업로드 폼 방식 요청 처리
파일업로드 폼 파일 요소값을 스프링MVC가 지원하는 MultipartFile 
매개변수로 처리함 
*/
@GetMapping("/registerForm")
public String registerForm() {
	log.info("registerForm에 왔다");
	
	//forwarding : /views/registerForm.jsp
	return "registerForm";
}

 

 

registerForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<form action="registerFile01" method="post"
	enctype="multipart/form-data">
	<p><input type="file" name="picture" /></p>
	<p><input type="submit" value="파일업로드" /></p>
</form>

 

결과 화면1

 

 

 

파일 업로드 시 필요 설정 작업

 

 

 

1. pom.xml

		<!-- 파일업로드 시작  -->
		<!-- common-fileupload 라이브러리는 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>
		<!-- 파일업로드 끝  -->

 

 

2. web.xml

src/main/webapp/WEB-INF/web.xml

		<!-- web.xml의 설정은 WAS(Tomcat) 자체 설정일 뿐임. -->
		<!-- multipart-config : 메모리사이즈, 업로드 파일 저장 위치, 최대 크기 설정 -->
		<multipart-config>
			<location>c:\\upload</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>

 

web.xml에 추가할 위치 참조1

 

 

	<!-- multipart 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>

 

web.xml에 추가할 위치 참조2

 

 

3. context.xml

Servers/Tomcat v8.5 Server at localhost-config(폴더 설정한 이름마다 다름)/context.xml

 

만약 WAS에 아래와 같이 설정하지 않으면 "Could not parse multipart servlet request" 에러발생,? WAS에서
Multipart를 찾지 못하게 된다

<Context allowCasualMultipartParsing="true" >
   <!-- 케시문제 해결 -->
   <Resources cachingAllowed="true" cacheMaxSize="100000"></Resources>

 

 

4. root-context.xml

하단에 beans 위에 추가

 

web.xml에서의 20MB까지 설정했어도 여기서 10MB까지로 한다면 10MB를 기준으로 한다.

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

 

 

 

파일 업로드 기능

 

 

  • 파일만 업로드

MemberController.java

하단에 추가

 

<input type="file" name="picture" />
name의 값이 파라미터의 변수명이랑 같아야함

/* 8. 파일업로드 폼 방식 요청 처리
파일업로드 폼 파일 요소값을 스프링MVC가 지원하는 MultipartFile 
매개변수로 처리함 
*/
@GetMapping("/registerForm")
public String registerForm() {
	log.info("registerForm에 왔다");
	
	//forwarding : /views/registerForm.jsp
	return "registerForm";
}

/*
요청URI : /registerFile01
요청파라미터 : {picture=파일객체}
요청방식 : post

<input type="file" name="picture" />
name의 값이 파라미터의 변수명이랑 같아야함
*/
@ResponseBody
@PostMapping("/registerFile01")
public String registerFile01(MultipartFile picture) {
	log.info("registerFile01에 왔다");
	
	// 원본 파일명
	String fileName = picture.getOriginalFilename();
	// 파일 크기
	long size = picture.getSize();
	// MIME타입
	String contentType = picture.getContentType();
	
	log.info("fileName : " + fileName);
	log.info("size : " + size);
	log.info("contentType : " + contentType);
	
	return "success";
}

 

 

registerForm.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

<!-- 
요청URI : /registerFile01
요청파라미터 : {picture=파일객체}
요청방식 : post
-->
<form action="/registerFile01" method="post"
	enctype="multipart/form-data">
	<p><input type="file" name="picture" /></p>
	<p><input type="submit" value="파일업로드" /></p>
</form>

 

결과 화면2-1

 

결과 화면2-2

 

결과 화면2-3

 

 

 

  • 파일 업로드 & 파라미터 추가(String)

파일업로드 폼 파일 요소값과 텍스트 필드 요소값을 MultipartFile 매개변수와 문자열 매개변수로 처리함

 

MemberController.java

/*
파일업로드 폼 파일 요소값과 텍스트 필드 요소값을 MultipartFile 매개변수와 문자열 매개변수로 처리함
요청URI : /registerFile02
요청파라미터 : {userId=a001,password=1234,picture=파일객체}
요청방식 : post
*/
@ResponseBody
@PostMapping("/registerFile02")
public String registerFile02(String userId, String password, 
		MultipartFile picture) {
	log.info("registerFile02에 왔다");
	
	log.info("userId : " + userId);
	log.info("password : " + password);
	
	// 원본 파일명
	String fileName = picture.getOriginalFilename();
	// 파일 크기
	long size = picture.getSize();
	// MIME타입
	String contentType = picture.getContentType();
	
	log.info("fileName : " + fileName);
	log.info("size : " + size);
	log.info("contentType : " + contentType);
	
	return "success";
	
}

 

 

registerForm.jsp

<!-- 
요청URI : /registerFile02
요청파라미터 : {userId=a001,password=1234,picture=파일객체}
요청방식 : post
-->
<form action="/registerFile02" method="post"
	enctype="multipart/form-data">
	<p>userId : <input type="text" name="userId" value="hongkd"></p>
	<p>password : <input type="password" name="password" value="1234"></p>
	<p><input type="file" name="picture"></p>
	<p><input type="submit" value="파일업로드"></p>
</form>

 

결과화면3-1

 

결과 화면3-2

 

 

 

  • 파일 업로드 & 파라미터 추가(자바빈즈 매개변수)

파일업로드 폼 파일 요소값과 텍스트 필드 요소값을 MultipartFile 매개변수와 자바빈즈 매개변수로 처리

 

 

MemberController.java

/*
파일업로드 폼 파일 요소값과 텍스트 필드 요소값을 MultipartFile 매개변수와 자바빈즈 매개변수로 처리

요청URI : /registerFile03
요청파라미터 : {userId=a001,password=1234,picture=파일객체}
요청방식 : post
 */
@ResponseBody
@PostMapping("/registerFile03")
public String registerFile03(MultipartFile picture,
		Member member) {
	log.info("registerFile03에 왔다");
	
	log.info("userId : " + member.getUserId());
	log.info("password : " + member.getPassword());
	
	// 원본 파일명
	String fileName = picture.getOriginalFilename();
	// 파일 크기
	long size = picture.getSize();
	// MIME타입
	String contentType = picture.getContentType();
	
	log.info("fileName : " + fileName);
	log.info("size : " + size);
	log.info("contentType : " + contentType);
	
	return "success";
}

 

 

registerForm.jsp

<!-- 
요청URI : /registerFile03
요청파라미터 : {userId=a001,password=1234,picture=파일객체}
요청방식 : post
-->
<form action="/registerFile03" method="post"
	enctype="multipart/form-data">
	<p>userId : <input type="text" name="userId" value="hongkd"></p>
	<p>password : <input type="password" name="password" value="1234"></p>
	<p><input type="file" name="picture"></p>
	<p><input type="submit" value="파일업로드"></p>
</form>

 

결과 화면4-1

 

결과 화면4-2

 

기존에 있던 데이터에서 덮어쓰기하여 저장됨

 

 

 

  • 파일 업로드(Member 자바빈즈 매개변수)

파일업로드 폼 파일 요소값과 텍스트 필드 요소값을 Member 타입의 자바빈즈 매개변수로 처리

+ 첨부파일 객체를 List로 값 받지 못함

 

 

MemberController.java

/*
파일업로드 폼 파일 요소값과 텍스트 필드 요소값을 Member 타입의 자바빈즈 매개변수로 처리
 */
@ResponseBody
@PostMapping("/registerFile04")
public String registerFile04(Member member) {
	log.info("registerFile04에 왔다");
	
	log.info("userId : " + member.getUserId());
	log.info("password : " + member.getPassword());
	
	
	MultipartFile picture = member.getPicture();
	
	// 원본 파일명
	String fileName = picture.getOriginalFilename();
	// 파일 크기
	long size = picture.getSize();
	// MIME타입
	String contentType = picture.getContentType();
	
	log.info("fileName : " + fileName);
	log.info("size : " + size);
	log.info("contentType : " + contentType);
	
	return "success";
}

 

 

registerForm.jsp

<!-- 
요청URI : /registerFile04
요청파라미터 : {userId=a001,password=1234,picture=파일객체}
요청방식 : post
-->
<form action="/registerFile04" method="post"
	enctype="multipart/form-data">
	<p>userId : <input type="text" name="userId" value="hongkd"></p>
	<p>password : <input type="password" name="password" value="1234"></p>
	<p><input type="file" name="picture"></p>
	<p><input type="submit" value="파일업로드"></p>
</form>

 

 

Member.java

vo 하단에 추가

	//스프링프레임워크에서 제공하는 MultipartFile 파일객체타입
	private MultipartFile picture;

 

결과 화면5-1

 

결과 화면5-2

 

 

 

  • 다중 파일 업로드

여러 개의 파일업로드 폼 파일 요소값을 여러 개의  MultipartFile 매개변수로 처리

 

 

MemberController.java

/*
여러 개의 파일업로드 폼 파일 요소값을 여러 개의  MultipartFile 매개변수로 처리

요청URI : /registerFile05
요청파라미터 : {picture=파일객체,pictur2=파일객체}
요청방식 : post
 */
@ResponseBody
@PostMapping("/registerFile05")
public String registerFile05(
		MultipartFile picture,
		MultipartFile picture2) {
	log.info("registerFile05에 왔다");
	
	// 원본 파일명
	String fileName = picture.getOriginalFilename();
	// 파일 크기
	long size = picture.getSize();
	// MIME타입
	String contentType = picture.getContentType();
	
	log.info("fileName : " + fileName);
	log.info("size : " + size);
	log.info("contentType : " + contentType);
	
	// ---------------------------------------------------------
	
	// 원본 파일명
	fileName = picture2.getOriginalFilename();
	// 파일 크기
	size = picture2.getSize();
	// MIME타입
	contentType = picture2.getContentType();
	
	log.info("fileName : " + fileName);
	log.info("size : " + size);
	log.info("contentType : " + contentType);
	
	return "success";
}

 

 

registerForm.jsp

<!-- 
요청URI : /registerFile05
요청파라미터 : {picture=파일객체,pictur2=파일객체}
요청방식 : post
-->
<form action="/registerFile05" method="post"
	enctype="multipart/form-data">
	<p><input type="file" name="picture" /></p>
	<p><input type="file" name="picture2" /></p>
	<p><input type="submit" value="파일업로드" /></p>
</form>
<hr />

 

결과 화면6-1

 

결과 화면6-2

 

 

 

  • 다중 파일 업로드 (배열)

List로는 가져오지 못함

 

 

파일업로드 폼 파일 요소값과 텍스트 필드 요소값을 Member 타입의 자바빈즈 배열 매개변수로 처리

 

 

MemberController.java

/*
파일업로드 폼 파일 요소값과 텍스트 필드 요소값을 
Member 타입의 자바빈즈 배열 매개변수로 처리

요청URI : /registerFile08
요청파라미터 : {userId=a001,password=1234,picture=파일객체}
요청방식 : post
 */
@ResponseBody
@PostMapping("/registerFile08")
public String registerFile08(Member member) {
	log.info("registerFile08에 왔다");
	
	log.info("userId : " + member.getUserId());
	log.info("password : " + member.getPassword());
	
	//스프링 파일 객체 배열 타입
	MultipartFile[] pictures = member.getPictures();
	
	String fileName = "";
	long size = 0;
	String contentType = "";
	
	for (MultipartFile multipartFile : pictures) {
		fileName = multipartFile.getOriginalFilename();
		size = multipartFile.getSize();
		contentType = multipartFile.getContentType();
		
		log.info("==================");
		log.info("fileName : " + fileName);
		log.info("size : " + size);
		log.info("contentType : " + contentType);
		log.info("==================");
	}
	
	return "success";
}

 

 

registerForm.jsp

<!-- 
요청URI : /registerFile08
요청파라미터 : {userId=a001,password=1234,picture=파일객체}
요청방식 : post
-->
<form action="/registerFile08" method="post"
	enctype="multipart/form-data">
	<p>userId : <input type="text" name="userId" value="hongkd"></p>
	<p>password : <input type="password" name="password" value="1234"></p>
	<p><input type="file" name="pictures" multiple></p>
	<p><input type="submit" value="파일업로드"></p>
</form>

 

 

Member.java

//<input type="file" name="pictures" multiple>
// 위의 input과 name 값이 변수명과 같아야 함
private MultipartFile[] pictures;

 

결과 화면7-1

 

결과 화면7-2

 

 

 

  • DB 연동 추가

 

테이블 추가

-- 첨부파일
CREATE TABLE ATTACH(
    GLOBAL_CODE VARCHAR2(50),
    SEQ NUMBER,
    FILENAME VARCHAR2(300),
    FILE_SIZE NUMBER,
    CONTENT_TYPE VARCHAR2(30),
    REG_DATE DATE,
    CONSTRAINT PK_ATTACH PRIMARY KEY(GLOBAL_CODE)
);

 

ATTACH 테이블 설명

 

복합키 설정해야 함

 

 

 

MemberController.java

/*
파일업로드 폼 파일 요소값과 텍스트 필드 요소값을 
Member 타입의 자바빈즈 배열 매개변수로 처리

요청URI : /registerFile08
요청파라미터 : {userId=a001,password=1234,picture=파일객체}
요청방식 : post
 */
@ResponseBody
@PostMapping("/registerFile08")
public String registerFile08(Member member) {
	log.info("registerFile08에 왔다");
	
	int result = this.memberService.registerFile08(member);
	log.info("registerFile08->result : " + result);
	
	return "success";
}

 

 

MemberService.java

package kr.or.ddit.service;

import kr.or.ddit.vo.Member;

public interface MemberService {
	
	//MEMBER, ADDRESS, CARD 테이블에 INSERT
	public int registerUserId(Member member);

	public int registerFile08(Member member);

}

 

 

MemberServiceImpl.java

//첨부파일 처리용 DataAccessObject
@Autowired
AttachDao attachDao;
    
    
@Override
public int registerFile08(Member member) {
	
	log.info("userId : " + member.getUserId());
	log.info("password : " + member.getPassword());
	
	//1) MEMBER 테이블에 insert
	// 등록란에 없기에 임의로 넣음
	member.setUserName("개똥이");
	member.setCoin(1000);
	int result = this.memberDao.registerUserId(member);
	log.info("registerFile08->result(1) : " + result);
	
	//스프링 파일 객체 배열 타입
	MultipartFile[] pictures = member.getPictures();
	
	String fileName = "";
	long size = 0;
	String contentType = "";
	
	int seq = 1;
	
	for (MultipartFile multipartFile : pictures) {
		fileName = multipartFile.getOriginalFilename();
		size = multipartFile.getSize();
		contentType = multipartFile.getContentType();
		
		log.info("==================");
		log.info("fileName : " + fileName);
		log.info("size : " + size);
		log.info("contentType : " + contentType);
		log.info("==================");
		
		//파일업로드 처리
		
		
		//2) ATTACH 테이블에 insert
		//{userId=a001,password=1234,picture=파일객체}
		AttachVO attachVO = new AttachVO();
		attachVO.setGlobalCode(member.getUserId()); //a001
		attachVO.setSeq(seq++);
		attachVO.setFilename(fileName);
		attachVO.setFileSize(size);
		attachVO.setContentType(contentType);
		attachVO.setRegDate(null);
		
		result += this.attachDao.insertAttach(attachVO);
	}
	
	return result;
}

 

 

AttachVO.java

package kr.or.ddit.vo;

import java.util.Date;

import lombok.Data;

@Data
public class AttachVO {
	private String globalCode;
	private int seq;
	private String filename;
	private long fileSize;
	private String contentType;
	private Date regDate;
}

 

 

mybatisAlias.xml

<typeAliases> 사이에 추가

<typeAlias type="kr.or.ddit.vo.AttachVO" alias="attachVO"/>

 

 

AttachDao.java

package kr.or.ddit.dao;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import kr.or.ddit.vo.AttachVO;

@Repository
public class AttachDao {

	@Autowired
	SqlSessionTemplate sqlSessionTemplate;
	
	//ATTACH 테이블에 insert
	public int insertAttach(AttachVO attachVO) {
		return this.sqlSessionTemplate.insert("attach.insertAttach", attachVO);
	}
}

 

 

attach_SQL.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="attach">
	<insert id="insertAttach" parameterType="attachVO">
		INSERT INTO ATTACH(GLOBAL_CODE, SEQ, FILENAME, FILE_SIZE, CONTENT_TYPE, REG_DATE)
		VALUES (#{globalCode}, #{seq}, #{filename}, #{fileSize}, #{contentType}, SYSDATE)
	</insert>
</mapper>

 

결과 화면8-1

 

결과 화면8-2

 

 

 

  • 폴더에 파일 업로드

LprodController.java

/* 
요청URI : /lprod/create
요청파라미터 : {lprodId=14, lprodGu=P501, lprodNm=분식류}
요청방식 : post
*/
@RequestMapping(value="/create", method=RequestMethod.POST)
public ModelAndView createPost(LprodVO lprodVO) {
	log.info("createPost->lprodVO : " + lprodVO);
	
	int result = this.lprodService.createPost(lprodVO);
	log.info("createPost->result : " + result);
	
	ModelAndView mav = new ModelAndView();
	
	// redirect : 새로운 URI 요청
	mav.setViewName("redirect:/lprod/create");
	
	return mav;
}

 

 

LprodServiceImpl.java

public class LprodServiceImpl implements LprodService {

	//데이터베이스 접근을 위해 LprodDao 인스턴스를 주입받자
	//DI(Dependency Injection):의존성 주입
	//IoC(Inversion of Control):제어의 역전
	@Autowired
	LprodDao lprodDao;
	
	//어디에 업로드? root-context.xml에 적어둠
	@Autowired
	String uploadFolder;
	
	@Autowired
	AttachDao attachDao;
	
	// 메서드 재정의
	@Override
	public int createPost(LprodVO lprodVO) {
		/*
			LprodVO(lprodId=76, lprodGu=P501, lprodNm=분식류, 
				productVOList=[
					ProductVO(productId=P5011, pname=라면, unitPrice=1, ..), 
					ProductVO(productId=P5012, pname=떡볶이, unitPrice=2, ..), 
					ProductVO(productId=P5013, pname=순대, unitPrice=3, ..)
				]
			)
		*/
		//1) LPROD 테이블에 insert
		int result = this.lprodDao.createPost(lprodVO);
		
		//2) PRODUCT 테이블에 insert
		List<ProductVO> productVOList = lprodVO.getProductVOList();
		for(ProductVO productVO : productVOList) {
			result += this.lprodDao.insertProduct(productVO);
		}
		
		//3) 파일업로드 및 ATTACH 테이블에 insert
		/*
		LprodVO(lprodId=76, lprodGu=P501, lprodNm=분식류, 
		   productVOList=[
		      ProductVO(productId=P5011, pname=라면, unitPrice=1, ..), 
		      ProductVO(productId=P5012, pname=떡볶이, unitPrice=2, ..), 
		      ProductVO(productId=P5013, pname=순대, unitPrice=3, ..)
		   ],
		   pictures=[파일객체1,파일객체2]
		)
		*/
		//연월일 폴더 처리 시작////
		//c:\\...\\upload + "\\" + 2024\\05\\27
		File uploadPath = new File(this.uploadFolder, getFolder());
		
		if(uploadPath.exists()==false) {
			uploadPath.mkdirs();
		}
		//연월일 폴더 처리 끝////
		
		//스프링 파일객체 타입의 배열로부터 파일객체를 하나씩 꺼내보자
		MultipartFile[] pictures = lprodVO.getPictures();
		
		String uploadFileName = ""; //업로드용
		String fileName = ""; //DB용
		int seq = 1;
		
		for (MultipartFile multipartFile : pictures) {
			log.info("---------------------");
			log.info("파일명 : " + multipartFile.getOriginalFilename());
			log.info("파일크기 : " + multipartFile.getSize());
			log.info("MIME : " + multipartFile.getContentType());
			
			uploadFileName = multipartFile.getOriginalFilename();
			
			//UUID
			UUID uuid = UUID.randomUUID();
			uploadFileName = uuid.toString() + "_" + uploadFileName;
			
			//파일 복사 계획(, : +"\\"+
			//File 객체 설계(복사할 대상 경로 , 파일명)
//          File saveFile = new File(uploadFolder, uploadFileName);
			//uploadPath : D:\\A_TeachingMaterial\\06_spring\\springProj\\src\\
			//               main\\webapp\\resources\\upload\\2022\\07\\22
			File saveFile = new File(uploadPath, uploadFileName);
			
			//파일 복사 실행
			try {
				multipartFile.transferTo(saveFile);
				
				// 2024\\05\\07 => /2024/05/07/asdfsa_개똥이.jpg
				fileName = "/" + getFolder().replace("\\", "/") + "/" + uploadFileName;
				
				AttachVO attachVO = new AttachVO();
				attachVO.setGlobalCode(lprodVO.getLprodGu());
				attachVO.setSeq(seq++);
				attachVO.setFilename(fileName);
				attachVO.setFileSize(multipartFile.getSize());
				attachVO.setContentType(multipartFile.getContentType());
				attachVO.setRegDate(null);
				
				log.info("attachVO : " + attachVO);
				
				result += this.attachDao.insertAttach(attachVO);
			} catch (IllegalStateException | IOException e) {
				log.error(e.getMessage());
			}
		}
		
		return result;
	}

	//연/월/일 폴더 생성
	public String getFolder() {
		//2024-05-03 형식(format) 지정
		//간단한 날짜 형식
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		
		//날짜 객체 생성(java.util 패키지)
		Date date = new Date();
		
		//2024-05-03
		String str = sdf.format(date);
		
		//2024-05-03 -> 2024\\05\\03
		//File.separator : 역슬레시
		return str.replace("-", File.separator);
	}
}

 

 

LprodVO.java

package kr.or.ddit.vo;

import java.util.List;

import org.springframework.web.multipart.MultipartFile;

import lombok.Data;

//POJO(Plain Old Java Object)에 위배
@Data
public class LprodVO {
	private int lprodId;
	private String lprodGu;
	private String lprodNm;
	
	//상품분류코드	: 상품		= 1 : N
	//LPROD		:PRODUCT	= 1 : N
	private List<ProductVO> productVOList;
	
	
	private MultipartFile[] pictures;
}

 

 

create.jsp

<!-- 
	요청URI : /lprod/create
	요청파라미터 : {lprodId=14,lprodGu=P501, lprodNm=분식류}
	요청방식 : post
 -->
 <form action="/lprod/create" method="post" enctype="multipart/form-data">
	 <p><input type="number" name="lprodId" placeholder="상품분류 아이디(ex. 14)" /></p>
	 <p>
	 	<input type="text" name="lprodGu" placeholder="상품분류 코드(ex. P501)" required />
	 	<button type="button" id="btnAuto" class="btn btn-primary btn-xs col-sm-2">상품코드 자동생성</button>
	 </p>
	 <p><input type="text" name="lprodNm" placeholder="상품분류 명(ex. 분식류)" /></p>
	 <hr />
	 <p><input type="file" name="pictures" id="pictures" multiple /></p>
	 <hr />
	 <p id="pFunc">
		<button type="button" class="btn btn-info btn-sm col-sm-1" id="btnPlus" >+</button>
		<button type="button" class="btn btn-danger btn-sm col-sm-1" id="btnMinus" >-</button>
	 </p>
	 
	 <!--
	 1) + : 영역이 하나 생김
	 2) - : 마지막 영역 삭제 
	  -->
	 <p class="pArea">
	 	<input type="text" name="productVOList[0].productId" id="productId0" class="form-control col-sm-3 float-left" placeholder="상품아이디">
	 	<input type="text" name="productVOList[0].pname" id="pname0" class="form-control col-sm-3 float-left" placeholder="상품명">
	 	<input type="text" name="productVOList[0].unitPrice" id="unitPrice0" class="form-control col-sm-3 float-left" placeholder="가격">
	 </p>
	 <p style="clear:both;"></p>
	 <p><input type="submit" id="btnSubmit" value="등록" /></p>
 </form>

 

결과 화면9 : _ 앞의 글자는 임의로 추가됨

 

 

 

  • 이미지 미리 보기

create.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="/resources/js/jquery.min.js"></script>
<title>Insert title here</title>
<script type="text/javascript">
//e : onchange 이벤트
function handleImg(e){
// 	<p id="pImg"></p> 영역에 이미지 미리보기를 해보자
	
	let files = e.target.files;
	
	//이미지가 여러개가 있을 수 있으므로 이미지들을 각각 분리해서 배열로 만듦
	let fileArr = Array.prototype.slice.call(files);
	
	fileArr.forEach(function(f){
		//이미지 파일이 아닌 경우 이미지 미리보기 실패 처리(MIME타입)
		if(!f.type.match("image.*")){
			alert("이미지 확장자만 가능합니다.");
			return;
		}
		
		//이미지 객체를 읽을 자바스크립트의 reader 객체 생성
		let reader = new FileReader();
		
		$("#pImg").html("");
		
		//e : reader가 이미지 객체를 읽는 이벤트
		reader.onload = function(e) {
			//e.target : f(이미지 객체)
			//e.target.result : reader가 이미지를 다 읽은 결과
			let img_html = "<img src=" + e.target.result + " style='width:30%;' />";
			//객체.append : 누적, .html : 새로고침, .innerHTML : J/S
			$("#pImg").append(img_html);
		}
		
		//f : 이미지 파일 객체를 읽은 후 다음 이미지 파일(f)을 위해 초기화 함
		reader.readAsDataURL(f);
	});
}

$(function(){
	//이미지 미리 보기 시작////////
	$("#uploadFile").on("change",handleImg);
	//이미지 미리 보기 끝////////
});
</script>
</head>
<body>
<h1>책 등록</h1>

<!-- 
요청URI : /crate
요청파라미터 : {title=개똥이의 모험, category=소설, price=12000}
요청방식 : post
-->
<form id="frm" action="/create" method="post" enctype="multipart/form-data">
	<p>제목 : <input type="text" name="title" required placeholder="제목"></p>
	<p>카테고리 : <input type="text" name="category" required placeholder="카테고리"></p>
	<p>가격 : <input type="number" name="price" required placeholder="가격"></p>
	<p id="pImg"></p>
	<p>도서이미지 : 
		<input type="file" id="uploadFile" name="pictures" multiple ></p>
	<p>
		<input type="submit" id="btnSave" value="저장">
	</p>
</form>

</body>
</html>

 

결과화면10 : 이미지 선택 시 사진 미리보기뜸

 

 

 

  • 컨트롤러로 분해(1대1 file 업로드시)

uploadOne 메소드로 따로 컨트롤러를 나눠 자주 사용하는 기능을 따로 관리하게 하였다.

 

 

create.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="/resources/js/jquery.min.js"></script>
<title>Insert title here</title>
<script type="text/javascript">
$(function(){
	$("#btnPlus").on("click",function(){
		let pAreaLen = $(".pArea").length;
		
		let str = "";
			str += "<p class='pArea'>";
			str += "<input type='text' name='licenseVOList["+pAreaLen+"].licNo' id='licNo"+pAreaLen+"' class='form-control col-sm-3 float-left' placeholder='자격증 번호'>";
			str += "<input type='text' name='licenseVOList["+pAreaLen+"].licNm' id='licNm"+pAreaLen+"' class='form-control col-sm-3 float-left' placeholder='자격증 명'>";
			str += "<input type='date' name='licenseVOList["+pAreaLen+"].licDt' id='licDt"+pAreaLen+"' class='form-control col-sm-3 float-left' placeholder='발급 일'>";
			str += "</p>";
			str += "<p style='clear:both;'></p>";
		
		$("#btnSubmit").parent().before(str);
		
	});
	
	$("#btnMinus").on("click",function(){
		let pAreaLen = $(".pArea").length;
		console.log("pAreaLen : ", pAreaLen);
		
		if(pAreaLen < 2) {
			alert("최소 하나는 존재해야 합니다.");
			return;
		}
		
		// next는 선택한 요소의 다음 형제 요소를 반환
		$(".pArea").last().next().remove();
		$(".pArea").last().remove();
	});
});
</script>
</head>
<body>
<h1>직원 등록</h1>

<h5>${title}</h5>

<form action="/employee/create" method="post" enctype="multipart/form-data">
	<p><input type="text" name="empNo" placeholder="직원 번호" required /></p>
	<p><input type="text" name="empName" placeholder="직원 명" required /></p>
	<p><input type="text" name="empAddress" placeholder="직원 주소" required /></p>
	<p><input type="text" name="empTelno" placeholder="직원 연락처" required /></p>
	<p><input type="number" name="empSalary" placeholder="직원 급여" required /></p>
	<!-- multiple 안씀 => EMPLOYEE : 증명사진 = 1 : 1 -->
	<p>
		<input type="file" id="uploadFile" name="uploadFile" placeholder="증명사진" />
		<label for="uploadFile" class="btn btn-info btn-sm col-sm-1">증명사진선택</label>
	</p>
	<p id="pImg"></p>
	
	<hr />
	<p id="pFunc">
		<button type="button" class="btn btn-info btn-sm col-sm-1" id="btnPlus" >+</button>
		<button type="button" class="btn btn-danger btn-sm col-sm-1" id="btnMinus" >-</button>
	</p>
	 
	<p class="pArea">
		<input type="text" name="licenseVOList[0].licNo" id="licNo0" class="form-control col-sm-3 float-left" placeholder="자격증 번호">
		<input type="text" name="licenseVOList[0].licNm" id="licNm0" class="form-control col-sm-3 float-left" placeholder="자격증 명">
		<input type="date" name="licenseVOList[0].licDt" id="licDt0" class="form-control col-sm-3 float-left" placeholder="발급 일">
	</p>
	<p style="clear:both;"></p>
	 
	<p><input type="submit" id="btnSubmit" value="등록" /></p>
</form>

</body>
</html>

 

 

EmployeeController.java

package kr.or.ddit.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.sun.org.apache.xpath.internal.operations.Mod;

import kr.or.ddit.service.EmployeeService;
import kr.or.ddit.vo.EmployeeVO;
import lombok.extern.slf4j.Slf4j;

// 스프링 프레임워크에게 컨트롤러임을 알려줌
// 스프링이 빈(bean,객체)으로 등록하여 관리
@Controller
@RequestMapping("/employee")
@Slf4j
public class EmployeeController {
	
	// 의존성 주입(Dependency Injection)
	@Autowired
	EmployeeVO myEmpVO;
	
	@Autowired
	EmployeeService employeeService;
	
	/*
	요청URI : /employee/create
	요청파라미터 : 
	요청방식 : get
	
	return 타입 : ModelaNDvIEW
	model.addObject("title","직원 등록");
	model.setViewName("employee/create");
	 */
	@RequestMapping(value="/create", method=RequestMethod.GET)
	public ModelAndView create() {
		
		ModelAndView mav = new ModelAndView();
		
		mav.addObject("title", "직원 등록");
		
		// 뷰리졸버
		// /WEB-INF/views/ + employee/create + .jsp
		mav.setViewName("employee/create");
		
		return mav;
	}
	
	/*
	요청URI : /employee/create
	요청파라미터 : {empNo=A011, empName=개똥이, empAddress=세종시 새롬중앙로 11
				, empTelno=010-5656-2222, empSalary=5000000, uploadFile=파일객체}
	요청방식 : post
	   
	return 타입 : ModelAndView
	model.setViewName("redirect:/employee/create");
	 */
	@RequestMapping(value="/create", method=RequestMethod.POST)
	public ModelAndView createPost(EmployeeVO employeeVO) {
		log.info("createPost -> employeeVO : " + employeeVO);
		
		// 하단의 문장을 사용하지 않아도 root-context.xml에 넣어둘 시 어디서든 접근 가능해짐
//		EmployeeVO myEmpVO = new EmployeeVO();
//		myEmpVO.setEmpNo("A000");
//		myEmpVO.setEmpName("김수현");
//		myEmpVO.setEmpAddress("대전 중구 문화동 1");
//		myEmpVO.setEmpTelno("010-111-2222");
//		myEmpVO.setEmpSalary(600000000);
//		myEmpVO.setFilename("A000.jsp");
		
//		log.info("주입된 myEmpVO : " + myEmpVO);
		
		int result = this.employeeService.createPost(employeeVO); 
		log.info("createPost->result : " + result);
		
		ModelAndView mav = new ModelAndView();
		mav.setViewName("redirect:/employee/create");
		
		return mav;
	}
}

 

 

EmployeeServiceImpl.java

package kr.or.ddit.service.impl;

import java.util.List;
import java.util.Map;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import kr.or.ddit.controller.UploadController;
import kr.or.ddit.dao.AttachDao;
import kr.or.ddit.dao.EmployeeDao;
import kr.or.ddit.service.EmployeeService;
import kr.or.ddit.vo.AttachVO;
import kr.or.ddit.vo.EmployeeVO;
import kr.or.ddit.vo.LicenseVO;
import lombok.extern.java.Log;
import lombok.extern.slf4j.Slf4j;
import sun.util.logging.resources.logging;

@Slf4j
@Service
public class EmployeeServiceImpl implements EmployeeService {

	@Autowired
	UploadController uploadController;
	
	@Autowired
	EmployeeDao employeeDao;
	
	@Autowired
	AttachDao attachDao;
	
	@Override
	public int createPost(EmployeeVO employeeVO) {
		/*
		EmployeeVO(empNo=A111, empName=김, empAddress=대전 1, empTelno=010-111-2222, empSalary=100, filename=null, 
		licenseVOList=[
			LicenseVO(empNo=null, licNo=A1111, licNm=언어영역, licDt=Fri May 03 00:00:00 KST 2024), 
			LicenseVO(empNo=null, licNo=A1112, licNm=수학영역, licDt=Sat May 04 00:00:00 KST 2024)
		])
		 */
		//1) EMPLOYEE 테이블에 insert
		//employeeVO.getUploadFile() : MultipartFile
		int result = this.employeeDao.createPost(employeeVO);
		
		List<LicenseVO> licenseVOList = employeeVO.getLicenseVOList();
		log.info("createPost->licenseVOList : " + licenseVOList);
		
		for (LicenseVO licenseVO : licenseVOList) {
			//2) LicenseVO(empNo=A111, licNo=A1111, licNm=언어영역, licDt=Fri May 03 00:00:00 KST 2024),
			licenseVO.setEmpNo(employeeVO.getEmpNo());
			result += this.employeeDao.insertLicenseVO(licenseVO);
		}
		
		//3) 파일업로드
		MultipartFile uploadFile = employeeVO.getUploadFile();
											// 파일 객체	, attachVO.globalCode
		result += uploadController.uploadOne(uploadFile, employeeVO.getEmpNo());
		
		//4) employeeVO.filename=null
		//글로벌 코드를 조건으로 하여 첫번쨰 첨부파일 객체를 가져옴
		AttachVO attachVO = this.attachDao.getFileName(employeeVO.getEmpNo());
		log.info("attachVO(2) : " + attachVO);
		
		//5) employeeVO.filename을 업데이트
		result += this.employeeDao.updateFileName(attachVO);
		
		return result;
	}
}

 

 

EmployeeDao.java

package kr.or.ddit.dao;

import java.util.List;
import java.util.Map;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import kr.or.ddit.vo.AttachVO;
import kr.or.ddit.vo.EmployeeVO;
import kr.or.ddit.vo.LicenseVO;


@Repository
public class EmployeeDao {

	@Autowired
	SqlSessionTemplate sqlSessionTemplate;
	
	public int createPost(EmployeeVO employeeVO) {
		return this.sqlSessionTemplate.insert("employee.createPost", employeeVO);
	}
    
	//5) employeeVO.filename을 업데이트
	public int updateFileName(AttachVO attachVO) {
		return this.sqlSessionTemplate.update("employee.updateFileName",attachVO);
	}

}

 

 

employee_SQL.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="employee">
	
	<insert id="createPost" parameterType="employeeVO">
		insert into employee(EMP_NO, EMP_NAME, EMP_ADDRESS, EMP_TELNO, EMP_SALARY
			<if test="filename!=null and filename!=''">
			, FILENAME
			</if>
		)
		values(#{empNo},#{empName},#{empAddress},#{empTelno},#{empSalary}
			<if test="filename!=null and filename!=''">
			,#{filename}
			</if>
		)
	</insert>
    
	<!-- employee -->
	<update id="updateFileName" parameterType="attachVO">
		UPDATE EMPLOYEE
		SET FILENAME = #{fileName}
		WHERE EMP_NO = #{globalCode}
	</update>
</mapper>

 

 

UploadController.java

package kr.or.ddit.controller;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.multipart.MultipartFile;

import kr.or.ddit.dao.AttachDao;
import kr.or.ddit.vo.AttachVO;
import lombok.extern.slf4j.Slf4j;

@Controller
@Slf4j
public class UploadController {
	
	@Autowired
	String uploadFolder;
	
	//static AttachDao attachDao => null이 메모리에 올라감
	@Autowired
	AttachDao attachDao;
	
	//연/월/일 폴더 생성
	public static String getFolder() {
		//2024-05-03 형식(format) 지정
		//간단한 날짜 형식
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
		
		//날짜 객체 생성(java.util 패키지)
		Date date = new Date();
		
		//2024-05-03
		String str = sdf.format(date);
		
		//2024-05-03 -> 2024\\05\\03
		//File.separator : 역슬레시
		return str.replace("-", File.separator);
	}

	//첨부파일 1개 업로드
	public int uploadOne(MultipartFile uploadFile, String globalCode) {
		int result = 0;
		
		// c:\\...\\upload + "\\" + 2024\\05\\07
		File uploadPath = new File(uploadFolder, getFolder());
		if(uploadPath.exists() == false) {
			uploadPath.mkdir();
		}
		
		log.info("-------------------");
		log.info("파일명 : " + uploadFile.getOriginalFilename());
		log.info("파일크기 : " + uploadFile.getSize());
		log.info("MIME : " + uploadFile.getContentType());
		
		String uploadFileName = uploadFile.getOriginalFilename();
		
		//UUID
		UUID uuid = UUID.randomUUID();
		//asdfqe_개똥이.jpg
		uploadFileName = uuid.toString() + "_" + uploadFileName;
		
		
		//복사 설계
		File saveFile = new File(uploadPath, uploadFileName);
		//복사 실행
		try {
			uploadFile.transferTo(saveFile);
			
			//웹경로
			String fileName = "/" + getFolder().replace("\\", "/") + "/" + uploadFileName;
			
			AttachVO attachVO = new AttachVO();
			attachVO.setGlobalCode(globalCode);
			attachVO.setSeq(1);
			attachVO.setFileName(fileName);
			attachVO.setFileSize(uploadFile.getSize());
			attachVO.setContentType(uploadFile.getContentType());
			attachVO.setRegDate(null);
			
			log.info("uploadOne->attachVO : " + attachVO);
			
			result = attachDao.insertAttach(attachVO);
			
		} catch (IllegalStateException | IOException e) {
			log.error(e.getMessage());
		}
		
		return result;
	}
}

 

 

AttachDao.java

package kr.or.ddit.dao;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import kr.or.ddit.vo.AttachVO;

@Repository
public class AttachDao {

	@Autowired
	SqlSessionTemplate sqlSessionTemplate;
	
	//ATTACH 테이블에 insert
	public int insertAttach(AttachVO attachVO) {
		return this.sqlSessionTemplate.insert("attach.insertAttach", attachVO);
	}
}

 

 

attach_SQL.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="attach">
	<insert id="insertAttach" parameterType="attachVO">
		INSERT INTO ATTACH(GLOBAL_CODE, SEQ, FILENAME, FILE_SIZE, CONTENT_TYPE, REG_DATE)
		VALUES (#{globalCode}, #{seq}, #{fileName}, #{fileSize}, #{contentType}, SYSDATE)
	</insert>
</mapper>

 

결과 화면11-1

 

결과 화면11-2

 

 

 

  • 컨트롤러로 분해(1대N file 업로드시)

ContUsServiceImpl.java

@Override
public int createPost(ContUsVO contUsVO) {
    int result = this.contUsDao.createPost(contUsVO);

    File uploadPath = new File(this.uploadFolder, UploadController.getFolder());

    if(uploadPath.exists()==false) {
        uploadPath.mkdir();
    }

    MultipartFile[] pictures = contUsVO.getPictures();

    String uploadFileName = "";
    String fileName = "";
    int seq = 1;

    //파일업로드 처리
    result += uploadController.uploadMulti(pictures, contUsVO.getCuCode());

    return result; 
}

 

 

UploadController.java

public int uploadMulti(MultipartFile[] pictures, String globalCode) {
  int result = 0;

  File uploadPath = new File(uploadFolder, getFolder());
  if(uploadPath.exists() == false) {
     uploadPath.mkdirs();
  }

  int seq = 1;
  for(MultipartFile picture: pictures) {
     UUID uuid = UUID.randomUUID();
     String uploadFileName = uuid.toString() + "_" + picture.getOriginalFilename();

     File saveFile = new File(uploadPath, uploadFileName);
     try {
        picture.transferTo(saveFile);

        String fileName = "/" + getFolder().replace("\\", "/") + "/" + uploadFileName;

        AttachVO attachVO = new AttachVO();
        attachVO.setGlobalCode(globalCode);
        attachVO.setSeq(seq++);
        attachVO.setFileName(fileName);
        attachVO.setFileSize(picture.getSize());
        attachVO.setContentType(picture.getContentType());
        attachVO.setRegDate(null);

        log.info("uploadOne->attachVO: " + attachVO);

        result += attachDao.insertAttach(attachVO);
        log.info("uploadOne->result: " + result);

     } catch (IllegalStateException | IOException e) {
        log.error(e.getMessage());
     }
  }

  return result;
}

 

 

반응형