열심히 끝까지

디바이스 융합 자바(Java) day74 - 파일 업로드 및 파일 미리보기 추가 본문

디바이스 융합 자바(Java)기반 풀스택 개발자 양성과정(수업내용)

디바이스 융합 자바(Java) day74 - 파일 업로드 및 파일 미리보기 추가

노유림 2022. 9. 27. 17:17

[오늘 진도]
[파일 업로드]
1. View
       등록한 이미지 미리보기는 view의 영역이라 JS로 했었음
       >> 나중에 다룰 예정
2. Command 객체(bVO)를 바꾸어주어야 함
       : 받는 쪽에서 못받게 되어있기 때문에 데이터를 받을 수 있게 수정해주어야 함

3. FileUpload 라이브러리(.jar) 추가


>> bVO에 
import org.springframework.web.multipart.MultipartFile;
private MultipartFile uploadFile;

추가

package com.ryo.biz.board;

import org.springframework.web.multipart.MultipartFile;

public class BoardVO {

	private MultipartFile uploadFile;
    
	public MultipartFile getUploadFile() {
		return uploadFile;
	}
	public void setUploadFile(MultipartFile uploadFile) {
		this.uploadFile = uploadFile;
	}
}


>> pom.xml에

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.1</version>
</dependency>

 

추가

4. MultipartResolver 설정해주어야 함
    DispatcherServlet-servlet.xml에 추가

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!-- 잘 작동하지 않을 경우 넣을 것 -->
        <!--<property name="defaultEncoding" value="UTF-8"></property>--> 
		<property name="maxUploadSize" value="100000" />
</bean>



1) bVO = new BVO();
2) bVO.setXxx();
    + multipartFile = new MultipartFile();
        >> 객체를 세팅하는 것이기 때문에 객체화를 해주어야 함
3) bVO.setXxx();

5. VIEW 파트에서 데이터 전송
    >> Command 객체에 세팅
       => 이 과정에서 내부적으로 new MultipartFile();을 해주어야 함
          ==   bVO.mf.(req.getParam())
       이 중간과정에서 파일을 new MF(); 해주는 역할은 "스프링 컨테이너"가 관리함!!!
          >> 사용자가 업로드한 파일 명도 받아올 수 있음
               업로드한 파일을 지정한 경로에 따로 저장도 가능(메서드 처리)
               업로드한 파일의 존재 여부 확인 가능

 

 

이미지 업로드는 [디바이스 융합 자바(Java) day54] 에서 공부 가능

이미지 미리보기는 [디바이스 융합 자바(Java) day55] 에서 공부 가능

 

 

["MultipartFile을 쓰지 않고 String값으로 requestParam으로 값 받아서 넘기는 것도 가능"]

 

>> 이미지 업로드 및 미리보기를 추가한 코드

 

BoardVO.java------------------------------------------------------------------

package com.ryo.biz.board;

import org.springframework.web.multipart.MultipartFile;

public class BoardVO {
	private int bid;
	private String title;
	private String writer;
	private String content;
	private int cnt;
	private String regdate;
	// 테이블에서는 사용하지 않는 항목
	private String searchCondition; // 검색에 TITLE/WRITER 받아오는 항목
	private String searchContent; // 검색한 내용을 받아오는 항목
	private MultipartFile uploadFile;
	private String fileName; // requestParam으로 값 받아서 넘기는 것도 가능[MultipartFile]
	
	public int getBid() {
		return bid;
	}
	public void setBid(int bid) {
		this.bid = bid;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getWriter() {
		return writer;
	}
	public void setWriter(String writer) {
		this.writer = writer;
	}
	public String getContent() {
		return content;
	}
	public void setContent(String content) {
		this.content = content;
	}
	public int getCnt() {
		return cnt;
	}
	public void setCnt(int cnt) {
		this.cnt = cnt;
	}
	public String getRegdate() {
		return regdate;
	}
	public void setRegdate(String regdate) {
		this.regdate = regdate;
	}
	public String getSearchCondition() {
		return searchCondition;
	}
	public void setSearchCondition(String searchCondition) {
		this.searchCondition = searchCondition;
	}
	public String getSearchContent() {
		return searchContent;
	}
	public void setSearchContent(String searchContent) {
		this.searchContent = searchContent;
	}
	
	public MultipartFile getUploadFile() {
		return uploadFile;
	}
	public void setUploadFile(MultipartFile uploadFile) {
		this.uploadFile = uploadFile;
	}
	@Override
	public String toString() {
		return "BoardVO [bid=" + bid + ", title=" + title + ", writer=" + writer + ", content=" + content + ", cnt="
				+ cnt + ", regdate=" + regdate + ", searchCondition=" + searchCondition + ", searchContent="
				+ searchContent + ", uploadFile=" + uploadFile + "]";
	}
	public String getFileName() {
		return fileName;
	}
	public void setFileName(String fileName) {
		this.fileName = fileName;
	}
}

BoardController.java------------------------------------------------------------------

package com.ryo.biz.controller;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;

//import javax.servlet.http.HttpServletRequest;
//import javax.servlet.http.HttpServletResponse;
//import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
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.SessionAttributes;
//import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.multipart.MultipartFile;

import com.ryo.biz.board.BoardService;
import com.ryo.biz.board.BoardVO;
//import com.ryo.biz.board.impl.BoardDAO;

@Controller
@SessionAttributes("data") // "data"라는 이름의 데이터가 Model 객체에 세팅이 된다면, 그 것을 session에 기억시키겠다.
public class BoardController{
	
	@Autowired
	private BoardService boardService; // 비즈니스 컴포넌트, DAO를 직접 이용하지 않을 것!

	// main에 넣을 구분값
	@ModelAttribute("scMap")
	public Map<String,String> searchConditionMap(){
		Map<String,String> scMap = new HashMap<String,String>();
		scMap.put("제목", "TITLE");
		scMap.put("작성자", "WRITER");
		return scMap;
	}
	
	// main
	// API 분석 때 필요할 것, VO에 넣는 것이 좋음
	@RequestMapping("/main.do")
	public String main(@RequestParam(value="searchCondition", defaultValue="TITLE", required=false)String searchCondition, @RequestParam(value="searchContent", defaultValue="", required=false)String searchContent,BoardVO bVO, Model model){
		
		// 검색을 위한 것
		if(bVO.getSearchCondition()==null) {
			bVO.setSearchCondition("TITLE");
		}
		if(bVO.getSearchContent()==null) {
			bVO.setSearchContent("");
		}
		System.out.println("검색조건: "+searchCondition);
	    System.out.println("검색어: "+searchContent);
		List<BoardVO> datas = boardService.selectAllBoard(bVO);
		
		
		model.addAttribute("datas", datas);
		return "main.jsp";
	}
	
	// 글 하나 선택
	@RequestMapping("/board.do")
	public String board(BoardVO bVO, Model model){

		bVO=boardService.selectOneBoard(bVO);
		// mav.addObject("data", bVO);
		model.addAttribute("data", bVO);
		return "board.jsp";
	}
	
	// 글 추가.jsp로 이동
	@RequestMapping(value="/binsert.do", method=RequestMethod.GET)
	public String binsertin() {
		return "insertBoard.jsp";
	}
	
	// 글 추가
	// get과 post일 때로 나눌 것 get은 이동 post일 때는 
	@RequestMapping(value="/binsert.do", method=RequestMethod.POST)
	public String binsert(BoardVO bVO) throws IllegalStateException, IOException{
		System.out.println("binsert 진행 : " + bVO);
		
		MultipartFile uploadFile = bVO.getUploadFile();
		if(!uploadFile.isEmpty()){// 업로드한 파일 존재여부 확인
			String fileName=uploadFile.getOriginalFilename(); // 업로드한 파일명
			bVO.setFileName(fileName);
			System.out.println(bVO.getFileName());
			uploadFile.transferTo(new File("사진을 저장할 주소"+fileName)); // 최종 저장  목적지 설정
		}
		
		boardService.insertBoard(bVO);
		
		return "redirect:main.do";
	}
	 

	// 글 삭제
	@RequestMapping("/bdelete.do")
	public String bdelete(BoardVO bVO) {
		
		boardService.deleteBoard(bVO);
		
		return "redirect:main.do";
	}
	
	// 글 업데이트
	@RequestMapping("/bupdate.do")
	public String bupdate(@ModelAttribute("data")BoardVO bVO) throws IllegalStateException, IOException{
		
		MultipartFile uploadFile = bVO.getUploadFile();
		if(!uploadFile.isEmpty()){// 업로드한 파일 존재여부 확인
			String fileName=uploadFile.getOriginalFilename(); // 업로드한 파일명
			bVO.setFileName(fileName);
			System.out.println(bVO.getFileName());
			uploadFile.transferTo(new File("사진을 저장할 주소값"+fileName)); // 최종 저장  목적지 설정
		}
		else {
			bVO.setFileName("곰돌이사진.png");
		}
		
		boardService.updateBoard(bVO);
		
		return "redirect:main.do";
	}
}

BoardDAO2.java------------------------------------------------------------------

package com.ryo.biz.board.impl;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;

import com.ryo.biz.board.BoardVO;

@Repository("boardDAO")
public class BoardDAO2 {
	
	// 의존 관계 + 의존성 주입 필요
	@Autowired
	private JdbcTemplate jdbcTemplate;
	
	final String sql_selectOne="SELECT * FROM BOARD WHERE BID=?";
	final String sql_selectAll="SELECT * FROM BOARD ORDER BY BID DESC";
	final String sql_selectAll_T="SELECT * FROM BOARD WHERE TITLE LIKE '%'||?||'%' ORDER BY BID DESC";
	final String sql_selectAll_W="SELECT * FROM BOARD WHERE WRITER LIKE '%'||?||'%' ORDER BY BID DESC";
	final String sql_insert="INSERT INTO BOARD(BID,TITLE,WRITER,CONTENT,UPLOADFILE) VALUES((SELECT NVL(MAX(BID),0)+1 FROM BOARD),?,?,?,?)";
	final String sql_update="UPDATE BOARD SET TITLE=?, CONTENT=?, UPLOADFILE=? WHERE BID=?";
	final String sql_delete="DELETE BOARD WHERE BID=?";

	// 글 하나 보기
	public BoardVO selectOneBoard(BoardVO vo) {
		Object[] args= {vo.getBid()};
		return jdbcTemplate.queryForObject(sql_selectOne,args,new BoardRowMapper());
	}
	// 글 모두 보기
	public List<BoardVO> selectAllBoard(BoardVO vo) {
		System.out.println("BoardDAO2 작동★");
		Object[] args = {vo.getSearchContent()};
		if(vo.getSearchCondition()==null) {
			return jdbcTemplate.query(sql_selectAll_T,args ,new BoardRowMapper());
		}
		else {
			return jdbcTemplate.query(sql_selectAll_W,args ,new BoardRowMapper());
		}
	}
	// 전체 및 검색 글을 보이는 내 방법
	public List<BoardVO> selectAllConditionBoard(BoardVO vo) {
		if(vo.getSearchCondition()==null) {
			vo.setSearchCondition("TITLE");
		}
		if(vo.getSearchContent()==null) {
			vo.setSearchContent("");
		}
		String sql_selectAll=sql_selectAll_T;
		if(vo.getSearchCondition().equals("WRITER")){
			sql_selectAll=sql_selectAll_W;
		}
		return jdbcTemplate.query(sql_selectAll, new BoardRowMapper(), vo.getSearchContent());	
	}
	
	
	// 글 추가
	public void insertBoard(BoardVO vo) {
		System.out.println("★");
		jdbcTemplate.update(sql_insert,vo.getTitle(),vo.getWriter(),vo.getContent(),vo.getFileName());
	}
	// 글 업데이트
	public void updateBoard(BoardVO vo) {
		jdbcTemplate.update(sql_update,vo.getTitle(),vo.getContent(),vo.getFileName(), vo.getBid());
	}
	// 글 삭제
	public void deleteBoard(BoardVO vo) {
		jdbcTemplate.update(sql_delete,vo.getBid());
	}	

}

// 글의 값들을 저장하는 Mapper
class BoardRowMapper implements RowMapper<BoardVO>{

	@Override
	public BoardVO mapRow(ResultSet rs, int rowNum) throws SQLException {
		// 알아서 rowNum을 판단하기 때문에 걱정하지 말 것
		BoardVO data=new BoardVO();
		data.setBid(rs.getInt("BID"));
		data.setContent(rs.getString("CONTENT"));
		data.setTitle(rs.getString("TITLE"));
		data.setWriter(rs.getString("WRITER"));
		data.setCnt(rs.getInt("CNT"));
		data.setFileName(rs.getString("UPLOADFILE"));
		data.setRegdate(rs.getString("REGDATE"));
		return data;
	}
	
}

insertBoard.jsp------------------------------------------------------------------

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<!--
	Stellar by HTML5 UP
	html5up.net | @ajlkn
	Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<html>
	<head>
		<title>insertBoard</title>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" />
		<link rel="stylesheet" href="assets/css/main.css" />
		<noscript><link rel="stylesheet" href="assets/css/noscript.css" /></noscript>
	</head>
	<body class="is-preload">

		<!-- Wrapper -->
			<div id="wrapper">

				<!-- Main -->
					<div id="main">

						<!-- Introduction -->
							<section id="intro" class="main">
								<div class="spotlight">
									<div class="content">
										<header class="major">
											<h2>write</h2>
										</header>
										<form action="binsert.do" method="post" enctype="multipart/form-data">
										<table class="alt">
												<tbody>
													<tr>
														<td>제목</td>
														<td colspan="2"><input type="text" name="title" value="제목" required></td>
													</tr>
													<tr>
														<td>작성자</td>
														<td colspan="2"><input type="text" name="writer" value="${user.name}" required readonly></td>
													</tr>
													<tr>
														<td>내용</td>
														<td colspan="2"><input type="text" name="content" value="글쓰기" required></td>
													</tr>
													<tr>
														<td>파일</td>
														<td colspan="2"><input type="file" name="uploadFile" onchange="loadFile(this);"></td>
													</tr>
													<tr>
														<td colspan="3" align="right"><input type="submit" class="button primary" value="글쓰기"> </td>
													</tr>
												</tbody>
										</table>
										</form>
										<a href="main.do">메인으로</a>
									</div>
									<span class="image"><img src="images/pic01.jpg"  alt="미리보기" id="preview" /></span>
								</div>
							</section>

					</div>

				<!-- Footer -->
					<footer id="footer">
						<section>
							<h2>Aliquam sed mauris</h2>
							<p>Sed lorem ipsum dolor sit amet et nullam consequat feugiat consequat magna adipiscing tempus etiam dolore veroeros. eget dapibus mauris. Cras aliquet, nisl ut viverra sollicitudin, ligula erat egestas velit, vitae tincidunt odio.</p>
							<ul class="actions">
								<li><a href="generic.html" class="button">Learn More</a></li>
							</ul>
						</section>
						<section>
							<h2>Etiam feugiat</h2>
							<dl class="alt">
								<dt>Address</dt>
								<dd>1234 Somewhere Road &bull; Nashville, TN 00000 &bull; USA</dd>
								<dt>Phone</dt>
								<dd>(000) 000-0000 x 0000</dd>
								<dt>Email</dt>
								<dd><a href="#">information@untitled.tld</a></dd>
							</dl>
							<ul class="icons">
								<li><a href="#" class="icon brands fa-twitter alt"><span class="label">Twitter</span></a></li>
								<li><a href="#" class="icon brands fa-facebook-f alt"><span class="label">Facebook</span></a></li>
								<li><a href="#" class="icon brands fa-instagram alt"><span class="label">Instagram</span></a></li>
								<li><a href="#" class="icon brands fa-github alt"><span class="label">GitHub</span></a></li>
								<li><a href="#" class="icon brands fa-dribbble alt"><span class="label">Dribbble</span></a></li>
							</ul>
						</section>
						<p class="copyright">&copy; Untitled. Design: <a href="https://html5up.net">HTML5 UP</a>.</p>
					</footer>

			</div>
<script type="text/javascript">
	function loadFile(input){
		//console.log('로그1');
		if(input.files && input.files[0]){ // 파일을 여러 개 들어올 것을 예상해서
			//console.log('로그2');
			var fr=new FileReader();
			fr.onload=function(event){
				//console.log=('로그3');
				document.getElementById('preview').src = event.target.result;
			};
			fr.readAsDataURL(input.files[0]);
		}
		else{
			// 필수 속성이니까 공백값을 주는 것이 기본
			//console.log('로그4');
			document.getElementById('preview').src="";
		}
	}
</script>


		<!-- Scripts -->
			<script src="assets/js/jquery.min.js"></script>
			<script src="assets/js/jquery.scrollex.min.js"></script>
			<script src="assets/js/jquery.scrolly.min.js"></script>
			<script src="assets/js/browser.min.js"></script>
			<script src="assets/js/breakpoints.min.js"></script>
			<script src="assets/js/util.js"></script>
			<script src="assets/js/main.js"></script>

	</body>
</html>

board.jsp------------------------------------------------------------------

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<!DOCTYPE HTML>
<!--
	Stellar by HTML5 UP
	html5up.net | @ajlkn
	Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<html>
<head>
<title>board</title>
<meta charset="utf-8" />
<meta name="viewport"
	content="width=device-width, initial-scale=1, user-scalable=no" />
<link rel="stylesheet" href="assets/css/main.css" />
<noscript>
	<link rel="stylesheet" href="assets/css/noscript.css" />
</noscript>
</head>
<body class="is-preload">

	<!-- Wrapper -->
	<div id="wrapper">

		<!-- Main -->
		<div id="main">

			<!-- Introduction -->
			<section id="intro" class="main">
				<div class="spotlight">
					<div class="content">
						<header class="major">
							<h2>board</h2>
						</header>
						<div class="table-wrapper">
							<form action="bupdate.do" method="post"
								enctype="multipart/form-data">
								<table class="alt">
									<tbody>
										<tr>
											<td>제목</td>
											<td colspan="2"><input type="text" name="title"
												value="${data.title}" required></td>
										</tr>
										<tr>
											<td>작성자</td>
											<td colspan="2">${data.writer}</td>
										</tr>
										<tr>
											<td>내용</td>
											<td colspan="2"><input type="text" name="content"
												value="${data.content}" required></td>
										</tr>
										<tr>
											<td>파일</td>
											<td colspan="2"><input type="file" name="uploadFile" onchange="loadFile(this);"></td>
										</tr>
										<tr>
											<td>글 업로드 시간</td>
											<td><input type="datetime-local" name="regdate"
												value="${data.regdate}"></td>
										</tr>
										<tr>
											<td colspan="3" align="right"><input type="submit"
												class="button primary" value="글 변경"><a href="bdelete.do?bid=${data.bid}"><input type="button"class="button primary" value="글 삭제"></a></td>
											
										</tr>
									</tbody>
								</table>
							</form>
							<a href="main.do">메인으로</a>
						</div>
					</div>
					<span class="image"><img src="images/${data.fileName}"
								alt="" /></span>
				</div>
			</section>

		</div>

		<!-- Footer -->
		<footer id="footer">
			<section>
				<h2>Aliquam sed mauris</h2>
				<p>Sed lorem ipsum dolor sit amet et nullam consequat feugiat
					consequat magna adipiscing tempus etiam dolore veroeros. eget
					dapibus mauris. Cras aliquet, nisl ut viverra sollicitudin, ligula
					erat egestas velit, vitae tincidunt odio.</p>
				<ul class="actions">
					<li><a href="generic.html" class="button">Learn More</a></li>
				</ul>
			</section>
			<section>
				<h2>Etiam feugiat</h2>
				<dl class="alt">
					<dt>Address</dt>
					<dd>1234 Somewhere Road &bull; Nashville, TN 00000 &bull; USA</dd>
					<dt>Phone</dt>
					<dd>(000) 000-0000 x 0000</dd>
					<dt>Email</dt>
					<dd>
						<a href="#">information@untitled.tld</a>
					</dd>
				</dl>
				<ul class="icons">
					<li><a href="#" class="icon brands fa-twitter alt"><span
							class="label">Twitter</span></a></li>
					<li><a href="#" class="icon brands fa-facebook-f alt"><span
							class="label">Facebook</span></a></li>
					<li><a href="#" class="icon brands fa-instagram alt"><span
							class="label">Instagram</span></a></li>
					<li><a href="#" class="icon brands fa-github alt"><span
							class="label">GitHub</span></a></li>
					<li><a href="#" class="icon brands fa-dribbble alt"><span
							class="label">Dribbble</span></a></li>
				</ul>
			</section>
			<p class="copyright">
				&copy; Untitled. Design: <a href="https://html5up.net">HTML5 UP</a>.
			</p>
		</footer>

	</div>
	
	<script type="text/javascript">
	function loadFile(input){
		//console.log('로그1');
		if(input.files && input.files[0]){ // 파일을 여러 개 들어올 것을 예상해서
			//console.log('로그2');
			var fr=new FileReader();
			fr.onload=function(event){
				//console.log=('로그3');
				document.getElementById('preview').src = event.target.result;
			};
			fr.readAsDataURL(input.files[0]);
		}
		else{
			// 필수 속성이니까 공백값을 주는 것이 기본
			//console.log('로그4');
			document.getElementById('preview').src="";
		}
	}
</script>

	<!-- Scripts -->
	<script src="assets/js/jquery.min.js"></script>
	<script src="assets/js/jquery.scrollex.min.js"></script>
	<script src="assets/js/jquery.scrolly.min.js"></script>
	<script src="assets/js/browser.min.js"></script>
	<script src="assets/js/breakpoints.min.js"></script>
	<script src="assets/js/util.js"></script>
	<script src="assets/js/main.js"></script>

</body>
</html>

BOARD.sql------------------------------------------------------------------

CREATE TABLE BOARD(
   BID INT PRIMARY KEY,
   TITLE VARCHAR(20) NOT NULL,
   WRITER VARCHAR(20) NOT NULL,
   CONTENT VARCHAR(100) NOT NULL,
   CNT INT DEFAULT 0,
   REGDATE DATE DEFAULT SYSDATE,
   UPLOADFILE VARCHAR(50) DEFAULT 'default.jpg'
);
SELECT*FROM BOARD;

 

-----------실행 결과