romworld

Spring 12 - Book 테이블도 파일업로드 해보자 본문

Spring

Spring 12 - Book 테이블도 파일업로드 해보자

inderrom 2023. 1. 30. 17:49

<aside.jsp>

 

 


도서 등록으로 이동했을 때 화면 깨짐을 수정해보자 !

 

<create.jsp>

  • input name 속성의 값을 content에서 cont 로 바꿔주고, 스크립트도 수정 후 다시 실행시키면 
  • 정상적으로 출력된다. (name이 충돌됐기 때문에)
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="/resources/ckeditor/ckeditor.js">
</script>
<meta charset="UTF-8">
<title>책 등록하기</title>
</head>
<body>
<h1>책 등록</h1>
<!-- 폼페이지 -->
<!-- 
요청URI : /cretae?title=롬이야기&category=소설&price=10000
요청파라미터 : tilte=롬이야기&category=소설&price=10000
요청방식 : post
 -->
<form action="/create" method="post">
	<!-- 폼데이터 -->
	<p>제목 : <input type="text" name="title" required /></p>
	<p>카테고리 : <input type="text" name="category" required /></p>
	<p>가격: <input type="number" name="price" required /></p>
	<p>내용: <textarea name="cont" rows="5" cols="30"></textarea></p>
	<p>
		<input type="submit" value="저장" />
		<input type="button" value="목록" />
	</p>
</form>
<script type="text/javascript">
	CKEDITOR.replace('cont');
	
</script>
</body>
</html>

 


이미지를 넣어보자!

<BookVO.java>

 

  • private String cont;
  • private MultipartFile[] multipartFiles;
  • private List<AttachVO> attachVOList;
package kr.or.ddit.vo;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

import org.springframework.web.multipart.MultipartFile;

// 자바빈 클래스
// 1) 멤버변수 2) 기본생성자 3) getter/setter메소드
public class BookVO {
	private int rnum;
	private int bookId;
	private String title;
	private String category;
	private int price;
	private Date insertDate;
	private String content;
	private String cont;
	private MultipartFile[] multipartFiles;
	//BookVO : AttachVO = 1 : N
	private List<AttachVO> attachVOList;
	
	// 기본생성자. 생략가능
	public BookVO() {}

	// getter/setter 메서드
	
	public int getRnum() {
		return rnum;
	}

	public void setRnum(int rnum) {
		this.rnum = rnum;
	}
	
	public int getBookId() {
		return bookId;
	}

	public void setBookId(int bookId) {
		this.bookId = bookId;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getCategory() {
		return category;
	}

	public void setCategory(String category) {
		this.category = category;
	}

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	public Date getInsertDate() {
		return insertDate;
	}

	public void setInsertDate(Date insertDate) {
		this.insertDate = insertDate;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public String getCont() {
		return cont;
	}

	public void setCont(String cont) {
		this.cont = cont;
	}

	public MultipartFile[] getMultipartFiles() {
		return multipartFiles;
	}

	public void setMultipartFiles(MultipartFile[] multipartFiles) {
		this.multipartFiles = multipartFiles;
	}

	public List<AttachVO> getAttachVOList() {
		return attachVOList;
	}

	public void setAttachVOList(List<AttachVO> attachVOList) {
		this.attachVOList = attachVOList;
	}

	@Override
	public String toString() {
		return "BookVO [rnum=" + rnum + ", bookId=" + bookId + ", title=" + title + ", category=" + category
				+ ", price=" + price + ", insertDate=" + insertDate + ", content=" + content + ", cont=" + cont
				+ ", multipartFiles=" + Arrays.toString(multipartFiles) + ", attachVOList=" + attachVOList + "]";
	}

}

 

<create.jsp>

  • enctype="multipart/form-data"
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="/resources/ckeditor/ckeditor.js">
</script>
<meta charset="UTF-8">
<title>책 등록하기</title>
</head>
<body>
<h1>책 등록</h1>
<!-- 폼페이지 -->
<!-- 
요청URI : /cretae?title=롬이야기&category=소설&price=10000
요청파라미터 : tilte=롬이야기&category=소설&price=10000
요청방식 : post
 -->
<form action="/create" method="post" enctype="multipart/form-data">
	<!-- 폼데이터 -->
	<div class="imgs_wrap"></div>
	<p>제목 : <input type="text" name="title" required /></p>
	<p>카테고리 : <input type="text" name="category" required /></p>
	<p>가격: <input type="number" name="price" required /></p>
	<p>내용: <textarea name="cont" rows="5" cols="30"></textarea></p>
	<p>
		책표지 : <input type="file" id="input_imgs" name="uploadfile" multiple />
	</p>
	<p>
		<input type="submit" value="저장" />
		<input type="button" value="목록" />
	</p>
</form>
<script type="text/javascript">
	CKEDITOR.replace('cont');
	
</script>
</body>
</html>

 

이미지 미리보기

<create.jsp>

  • 이미지 미리보기 스크립트 추가
<script type="text/javascript">
$(function(){
	// 이미지 미리보기 시작 -
	$("#input_imgs").on("change", handleImgFileSelect);
	// e: change 이벤트
	function handleImgFileSelect(e){
		// 파일객체에 파일들
		let files = e.target.files;
		// 이미지 배열
		let fileArr = Array.prototype.slice.call(files);
		// fileArr에서 하나 꺼내면 f(파일객체 1개)
		fileArr.forEach(function(f){
			// 이미지만 가능
			if(!f.type.match("image.*")){
				alert("이미지 확장자만 가능합니다.")
				return;
			}
			// 이미지를 읽을 객체
			let reader = new FileReader();
			//reader.readAsDataURL(f);의 이벤트
			reader.onload = function(e){
				let img_html = "<img src=\"" + e.target.result +"\" stype='width:30%' />";
				
				$(".imgs_wrap").append(img_html);
			}
			// 이미지를 읽는다.
			reader.readAsDataURL(f);
		});// end forEach
	}
	// 이미지 미리보기 끝 -
})
</script>

 


<BookController.java>

  • createPost 메서드 수정
  • // cont -> content로 복사
    bookVO.setContent(bookVO.getCont());
  • LprodController.getFolder() 는 스태틱이므로 접근이 가능하다.
  • LprodController.checkImageType 도 스태틱이므로 접근 가능
/*
	 요청URI : /cretae?title=롬이야기&category=소설&price=10000
	요청URL : /create
	요청파라미터 : {title=롬이야기&category=소설&price=10000}
	요청방식 : post
	
	bookVO{bookId:null,title:롬이야기,category:소설price:10000,insertDate:null
					content:null, cont: 내용, uploadfile:파일객체, attachVOList:null}
	private int bookId;
	private String title;
	private String category;
	private int price;
	private Date insertDate;
	private String content;
	private String cont;
	private MultipartFile[] uploadfile;
	private List<AttachVO> attachVOList;
	*/
	@RequestMapping(value="/create", method=RequestMethod.POST)
	public ModelAndView createPost(BookVO bookVO, ModelAndView mav) {
		log.info("bookBO : " + bookVO.toString());
	
		
		//<selectKey resultType="int" order="BEFORE" keyProperty="bookId">
		//1 증가된 기본키 값을 받음
		int bookId = bookService.createPost(bookVO);
		
		log.info("bookId :" + bookId);
		
		if(bookId <1) { // 등록 실패
			// /create로 요청을 다시 함 => uri주소가 바뀜
			mav.setViewName("redirect:/create");
			
		}else {// 등록 성공 : 상세보기 페이지로 이동
			mav.setViewName("redirect:/detail?bookId=" + bookId);
		}
		
		return mav;
	}

 

<book_SQL.xml>

  • lprod_SQL.xml에서 작성한 다중 insert를 가져온다
	
	<!-- 다중 insert 시 update 태그를 사용
		파라미터 : List<AttachVO attachVOList
		insert, update, delete의 경우 resultType은 생략
		separator=" " : 공백으로 구분
		-->
		<update id="createPostAttach" parameterType="java.util.List">
		<!-- 
		order="BEFORE"는 foreach 태그 실행 전에 selectKey부터 실행
		 -->
			<selectKey resultType="int" order="BEFORE" keyProperty="seq">
				SELECT NVL(MAX(SEQ),0)+1 FROM ATTACH
			</selectKey>
			<!-- index : 반복 시 index값. 0부터 시작. -->
		<foreach collection="list" item="attachVO"
				open="INSERT ALL" close="SELECT * FROM DUAL" separator=" "
				index="idx" >
				INTO ATTACH(SEQ, FILENAME, FILESIZE, THUMBNAIL, REGDATE, ETP_ID) 
				VALUES(#{idx}+#{seq},#{attachVO.filename},#{attachVO.filesize},'',sysdate,
					#{attachVO.etpId})
			</foreach>
		</update>

 

<BookDao.java> 추가

// ATTACH 테이블에 insert all
	public int createPostAttach(List<AttachVO> attachVOList) {
		return this.sqlSessionTemplate.update("book.createPostAttach", attachVOList);
	}

 

<BookServiceImpl.java>

  • 컨트롤러에서 작성한 소스 잘라내서 붙히고 작성
package kr.or.ddit.service.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
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.LprodController;
import kr.or.ddit.dao.BookDao;
import kr.or.ddit.service.BookService;
import kr.or.ddit.vo.AttachVO;
import kr.or.ddit.vo.BookVO;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnailator;

// 서비스 클래스 : 비즈니스 로직
// 스프링 MVC 구조에서 Controller와 DAO를 연결하는 역할

/*
스프링 프레임워크(디자인패턴 + 라이브러리)는 직접 클래스를 생성하는 것을 지양(싫음)하고,
인터페이스를 통해 접근하는 것을 권장하고 있기 때문에(확장성)
프링이는 인터페이스를 좋아해 ..
그래서 서비스 레이어는 인터페이스(BookService)와 클래스(BookServiceImpl)를 함께 사용함

Impl : implement의 약자
*/
// Service 어노테이션 : "프링아 이 클래스는 서비스 클래스야" 라고 알려줌.
//					 프링이가 자바빈으로 등록해줌
@Slf4j
@Service
public class BookServiceImpl implements BookService {
	
	// 데이터베이스에 접근하기 위해 BookDao 인스턴스를 주입받자
	@Autowired
	BookDao bookDao;
	
	// 도서 테이블(Book)에 입력
	//<insert id="createPost" parameterType="bookVO"> insert id와 메서드 명을 같게 설정
	@Override
	public int createPost(BookVO bookVO) {
		//1) Book 테이블에 insert. bookId를 리턴받음
		// bookVO의 bookId 멤버변수에 새로운 값이 들어있다.
		int result =  bookDao.createPost(bookVO);
		
		// cont -> content로 복사
		bookVO.setContent(bookVO.getCont());
		
		String uploadFolder = "C:\\eclipse_202006\\workspace\\springProj\\src\\main\\webapp\\resources\\upload";
		
		// make folder 시작 -
		File uploadPath = new File(uploadFolder , LprodController.getFolder());
		log.info("uploadPath : " + uploadPath);
		
		//만약 연/월/일 해당 폴더가 없다면 생성
		if(uploadPath.exists()==false) {
			uploadPath.mkdirs();
		}
		// make folder 끝 -
		
		//List<AttachVO> attachVOList;에 데이터 넣기 시작 -
		List<AttachVO> voList = new ArrayList<AttachVO>();
		//<input type="file" id="input_imgs" name="uploadfile" nultiple />
		MultipartFile[] multipartFiles = bookVO.getUploadfile();
		
		// multipartFile : 파일객체1개
		for(MultipartFile multipartFile : multipartFiles) {
			AttachVO vo = new AttachVO();
			// 실제 파일명
			String uploadFileName = multipartFile.getOriginalFilename();
			
			log.info("----------");
			log.info("filename : " + uploadFileName);
			log.info("filesize : " + multipartFile.getSize());
			log.info("contentType : " + multipartFile.getContentType());
			
			
			//UUID 처리 시작 -
			UUID uuid = UUID.randomUUID();
			uploadFileName = uuid.toString() + "_" + uploadFileName;
			//UUID 처리 끝 -
			
			// File 객체 설계(복사할 경로, 파일명)
			File saveFile = new File(uploadPath, uploadFileName);
			
			try {
				// 파일 복사. 파일객체를 복사한다(대상경로 및 파일명으로)
				multipartFile.transferTo(saveFile);
				
				// 이미지인지 확인
				if(LprodController.checkImageType(saveFile)) {
					// 썸네일 파일설계
					FileOutputStream thumbnail = new FileOutputStream(
							new File(uploadPath, "s_" + uploadFileName)
							);
					//썸네일 생성
					Thumbnailator.createThumbnail(multipartFile.getInputStream(),
							thumbnail,100,100);
					thumbnail.close();
				}
			}catch(IllegalStateException e){
				log.error(e.getMessage());
			}catch(IOException e) {
				log.error(e.getMessage());
			}
			// /2023/01/30/asdfasd_새롬이.jpg
			String filename = "/" + LprodController.getFolder().replace("\\", "/") + "/" +
					uploadFileName;
			vo.setFilename(filename);
			Long l = multipartFile.getSize();
			vo.setFilesize(l.intValue());
			// /2023/01/30/s_asdfasd_새롬이.jpg
			String thumbFilename = "/" + LprodController.getFolder().replace("\\", "/") + "/s_" +
					uploadFileName;
			vo.setThumbnail(thumbFilename);
			// 전사적 아이디
			vo.setEtpId(bookVO.getBookId()+"");
			
			voList.add(vo);
			
		} //end for
		
		
		//ATTACH 테이블에 insert하기 위한 최종목적
		bookVO.setAttachVOList(voList);
		//List<AttachVO> attachVOList;에 데이터 넣기 끝 -
		
		//2) BookDao의 createPostAttach(List<AttachVO> attachVOList) 메소드 호출
		this.bookDao.createPostAttach(bookVO.getAttachVOList());
		
		return result;
	}
	
	// 책 상세보기
	@Override
	public BookVO detail(BookVO bookVO) {
		return bookDao.detail(bookVO);
	}
	
	// 책 수정하기
	@Override
	public int updatePost(BookVO bookVO) {
		return this.bookDao.updatePost(bookVO);
	}
	
	// 책 삭제하기
	@Override
	public int deletePost(BookVO bookVO) {
		return this.bookDao.deletePost(bookVO);
	}
	
	// 책 목록
	// 리턴 타입 :List<BookVO>
	@Override
	public List<BookVO> list(String keyword){
		return this.bookDao.list(keyword);
	}
}

 

실행시키면

 

파일을 선택해서 실행해보면 detail로 넘아가고

데이터 베이스에도 데이터가 잘 들어간다.

Comments