파일 업로드 폼 방식 요청 처리
: 파일업로드 폼 파일 요소값을 스프링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. 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>
<!-- 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>
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>
- 파일 업로드 & 파라미터 추가(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>
- 파일 업로드 & 파라미터 추가(자바빈즈 매개변수)
파일업로드 폼 파일 요소값과 텍스트 필드 요소값을 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>
- 파일 업로드(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;
- 다중 파일 업로드
여러 개의 파일업로드 폼 파일 요소값을 여러 개의 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 />
- 다중 파일 업로드 (배열)
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;
- 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)
);
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>
- 폴더에 파일 업로드
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>
- 이미지 미리 보기
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>
- 컨트롤러로 분해(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>
- 컨트롤러로 분해(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;
}
'스프링 프레임워크' 카테고리의 다른 글
[스프링 프레임워크] 19장 파일업로드 Ajax 방식 요청 처리 (0) | 2024.05.08 |
---|---|
[스프링 프레임워크] 18장 Ajax 방식 요청 처리 (0) | 2024.05.08 |
[스프링 프레임워크] 16장 컨트롤러 요청 처리3(폼 필드 요소 값을 매개변수로 처리) (1) | 2024.05.02 |
[스프링 프레임워크] 15장 컨트롤러 요청 처리2(Date 타입 처리, DateTimeFormat 애너테이션) (1) | 2024.05.01 |
[스프링 프레임워크] 14장 컨트롤러 요청 처리1(컨트롤러 메서드 매개변수, 요청 처리, 요청 데이터 처리 애너테이션, 요청 처리 자바빈즈) (0) | 2024.05.01 |