본문 바로가기
ICIA 수업일지

2021.10.13 수업일지(Spring Framework, fileupload)

by 주성씨 2021. 10. 13.

- 파일 업로드를 이용해서 상품등록하는 게시판을 만들겠다.

- 우선 테이블을 만들겠다.

- go to mysql

- root에 들어가서 DB를 새로 만들겠다.

# 2021.10.13
create user 'product'@'localhost' identified by 'pass';
create database productdb;
grant all privileges on productdb.* to 'product'@'localhost';

확인

 

- 테이블 생성

#2021.10.13
create table tbl_product(
	pcode char(4) primary key not null,
    pname nvarchar(200) not null,
    price int default 0,
    image nvarchar(200)
);

desc tbl_product;

 

확인

 

- 테이블에 데이터를 insert하겠다.

insert into tbl_product(pcode, pname, price, image)
values('P110', '[14k Gold] 스테디 원터치 링귀걸이', 99900, 'img01.jpg');
insert into tbl_product(pcode, pname, price, image)
values('P111', '[14kGold] 델리아 원터치 링귀걸이', 69900, 'img02.jpg');
insert into tbl_product(pcode, pname, price, image)
values('P112', '[925실버] 샤인모션 원터치 링귀걸이', 20900, 'img03.jpg');
insert into tbl_product(pcode, pname, price, image)
values('P113', '[925실버] 볼앤하트 원터치 링귀걸이', 17900, 'img04.jpg');
insert into tbl_product(pcode, pname, price, image)
values('P114', '[14kGold] 셔링 귀걸이', 49900, 'img05.jpg');
insert into tbl_product(pcode, pname, price, image)
values('P115', '[14kGold] 메르시 목걸이', 15900, 'img06.jpg');
insert into tbl_product(pcode, pname, price, image)
values('P116', '[925실버] 트윈 원터치 링귀걸이', 20800, 'img07.jpg');
insert into tbl_product(pcode, pname, price, image)
values('P117', '[14kGold] 한쪽 베이직 금볼 귀걸이', 7900, 'img08.jpg');

 

- 확인

select * from tbl_product order by pcode desc;

 

- 해당 경로에 다운받은 이미지 삽입 C:\data\product

jewel.zip
0.46MB
확인

 

 

- 이제 프로젝트를 생성하겠다.

- setup 프로젝트를 복붙해서 ex07 프로젝트를 생성한다.

- 우선 디비 설정부터 해주겠다.

/ex07/src/main/webapp/WEB-INF/spring/root-context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd">
	<!-- DB 변경부 -->
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="net.sf.log4jdbc.sql.jdbcapi.DriverSpy"></property>
		<property name="url" value="jdbc:log4jdbc:mysql://127.0.0.1:3306/productdb"></property>
		<property name="username" value="product"></property>
		<property name="password" value="pass"></property>
	</bean>
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="mapperLocations" value="classpath:/mapper/**/*.xml" />
	</bean>
	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"
		destroy-method="clearCache">
		<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
	</bean>
	<context:component-scan base-package="com.example.mapper"></context:component-scan>
	<context:component-scan base-package="com.example.service"></context:component-scan>
	<bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<tx:annotation-driven />
</beans>

 

- 업로드 path를 변경하겠다.

/ex07/src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml

.....
	<!-- 파일 업로드 path 변경부 -->
	<beans:bean id="uploadPath" class="java.lang.String">
		<beans:constructor-arg value="c:/data/product" />
	</beans:bean>
</beans:beans>

 

- 변경 후 서버 모듈을 설정해준다.

정상구동 확인

 

 

- ProductVO를 생성한다.

/ex07/src/main/java/com/example/domain/ProductVO.java

package com.example.domain;

public class ProductVO {
	private String pcode;
	private String pname;
	private int price;
	private String image;
    ... getter, setter, tostring

 

- MysqlMapper를 복사해서 ProductMapper를 만든다.

/ex07/src/main/resources/mapper/ProductMapper.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="com.example.mapper.ProductMapper">
	<select id="list" resultType="com.example.domain.productVO">
		select * from tbl_product
</mapper>

 

- 인터페이스를 생성한다.

/ex07/src/main/java/com/example/mapper/ProductDAO.java

package com.example.mapper;

import java.util.List;

import com.example.domain.ProductVO;

public interface ProductDAO {
	public List<ProductVO> list();
}

 

- 구현체를 생성한다.

/ex07/src/main/java/com/example/mapper/ProductDAOImpl.java

package com.example.mapper;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.example.domain.ProductVO;

@Repository
public class ProductDAOImpl implements ProductDAO {
	@Autowired
	SqlSession session;
	
	String namespace="com.example.mapper.ProductDAO";
	
	@Override
	public List<ProductVO> list() {
		return session.selectList(namespace+".list");
	}

}

 

- 테스트

/ex07/src/test/java/com/example/controller/MysqlTest.java

package com.example.controller;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.example.mapper.MysqlMapper;
import com.example.mapper.ProductDAO;

@RunWith(SpringJUnit4ClassRunner.class) // 먼저 SpringJUnit4ClassRunner.class
										// import한다.
@ContextConfiguration(locations={"file:src/main/webapp/WEB-INF/spring/**/*.xml"})
public class MysqlTest {
	// 자동으로 연결
	@Autowired
	private ProductDAO dao;

	// 연결해서 getList을 테스트
	@Test
	public void getList() {
		dao.list();
	}
}

 

확인

 

- 이제 CRUD작업을 DAO에 할 수 있도록 하겠다. 인터페이스라 메서드에 대한 형태의 갖춰주는게 가능하다.

/ex07/src/main/java/com/example/mapper/ProductDAO.java

package com.example.mapper;

import java.util.List;

import com.example.domain.ProductVO;

public interface ProductDAO {
	public List<ProductVO> list();
    //new
	public ProductVO read(String pcode);
	public void insert(ProductVO vo);
	public void update(ProductVO vo);
	public void delete(String pcode);
}

 

- SQL문을 작성하겠다.

/ex07/src/main/resources/mapper/ProductMapper.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="com.example.mapper.ProductDAO">
	<!-- 테이블 데이터 불러오기 -->
	<select id="list" resultType="com.example.domain.ProductVO">
		select * from tbl_product;
	</select>
	<!-- 특정 값 불러오기 -->
	<select id="read" resultType="com.example.domain.ProductVO">
		select * from tbl_product
		where pcode=#{pcode}
	</select>
	<!-- insert -->
	<insert id="insert">
		insert into tbl_product(pcode,pname,price,image)
		values(#{pcode),#{pname},#{price},#{image})
	</insert>
	<!-- update -->
	<update id="update">
		update tbl_product set pname=#{pname}, price=#{price}, image=#{image}
		where pcode=#{pcode}
	</update>
	<!-- delete -->
	<delete id="delete">
		delete from tbl_product
		where pcode=#{pcode} 
	</delete>
</mapper>

// 비교요망
<?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="com.example.mapper.ProductDAO">
	<select id="list" resultType="com.example.domain.ProductVO">
		select * from tbl_product;
	</select>
	<select id="read" resultType="com.example.domain.ProductVO">
		select * from tbl_product
		where pcode=#{pcode}
	</select>
	<insert id="insert">
		insert into tbl_product(pcode,pname,price,image)
		values(#{pcode}, #{pname}, #{price}, #{image})
	</insert>
	<update id="update">
		update tbl_product set pname=#{pname},price=#{price},image=#{image}
		where pcode=#{pcode}
	</update>
	<delete id="delete">
		delete from tbl_product
		where pcode=#{pcode}
	</delete>
</mapper>

 

/ex07/src/main/java/com/example/mapper/ProductDAOImpl.java

package com.example.mapper;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.example.domain.ProductVO;

@Repository
public class ProductDAOImpl implements ProductDAO {
	@Autowired
	SqlSession session;
	
	String namespace="com.example.mapper.ProductDAO";
	
	@Override
	public List<ProductVO> list() {
		return session.selectList(namespace+".list");
	}

	@Override
	public ProductVO read(String pcode) {
		return session.selectOne(namespace+".read",pcode);
	}

	@Override
	public void insert(ProductVO vo) {
		session.insert(namespace+".insert",vo);
	}

	@Override
	public void update(ProductVO vo) {
		session.update(namespace+".update",vo);
	}

	@Override
	public void delete(String pcode) {
		session.delete(namespace+".delete",pcode);
	}

}

 

- 이제 컨트롤러를 만들겠다. 기존에 있던 홈컨트롤러를 이용하자.

/ex07/src/main/java/com/example/controller/HomeController.java

package com.example.controller;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		return "home";
	}
}

 

- 이전에 사용했던 CSS

/ex07/src/main/webapp/resources/home.css

@CHARSET "UTF-8";

@font-face {
	font-family: 'SpoqaHanSansNeo-Regular';
	src:
		url('https://cdn.jsdelivr.net/gh/projectnoonnu/noonfonts_2108@1.1/SpoqaHanSansNeo-Regular.woff')
		format('woff');
	font-weight: normal;
	font-style: normal;
}

body {
	font-family: 'SpoqaHanSansNeo-Regular';
}

#page {
	width: 960px;
	padding: 20px;
	margin: 0px auto;
}

#header {
	width: auto;
	padding: 10px;
	margin-bottom: 20px;
	text-align: center;
}

#center {
	width: auto;
	border: 1px solid black;
	padding: 10px;
	margin-bottom: 20px;
}

#content {
	text-align: center
}

#footer {
	width: auto;
	border: 1px solid black;
	padding: 10px;
	text-align: center;
}

#menu a:nth-child(n) {
	text-decoration: none;
	padding: 10px 20px 10px 20px;
}

#menu a:visited {
	color: black;
}

.pagination {
	display: inline-block;
	margin-top: 20px;
}

.pagination a {
	color: black;
	float: left;
	padding: 8px 16px;
	text-decoration: none;
}

.pagination a.active {
	background-color: #4CAF50;
	color: white;
}

.pagination a:hover {
	background: gray;
	color: white;
}

h1 {
	text-align: center;
}

table {
	border-collapse: collapse;
	margin: 0px auto;
}

td {
	border: 1px solid gray;
}

.title {
	background: gray;
	color: white;
	text-align: center;
}

.row:hover {
	cursor: pointer;
	background: gray;
	color: white;
}

.btn {
	text-align: center;
}

 

- 뷰를 수정한다.

/ex07/src/main/webapp/WEB-INF/views/home.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ page session="false"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<html>
<head>
<title>Home & Shopping</title>
<link rel="stylesheet" href="/resources/home.css" />
<script src="http://code.jquery.com/jquery-3.1.1.min.js"></script>
<script
	src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/3.0.1/handlebars.js"></script>
</head>
<body>
	<div id="page">
		<div id="header">
			<img src="/resources/back.png" width=960 />
		</div>
		<div id="center">
			<div id="menu">
				<a href="">상품등록</a>
			</div>
			<div id="content">
				<jsp:include page="${pageName}"></jsp:include>
			</div>
		</div>
		<div id="footer">
			<h3>Copyright H&S. All rights Reserved.</h3>
		</div>
	</div>
</body>
</html>

 

확인

 

- 이제 메인페이지에 상품목록을 출력하도록 하겠다

- 목록을 출력하는 페이지를 만들기 이전에 컨트롤러를 만들겠다.

- 홈컨트롤러를 이용해서 출력하겠다.

package com.example.controller;

import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HomeController {

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String home(Locale locale, Model model) {
		model.addAttribute("pageName", "list.jsp");
		return "home";
	}
}

 

- json 데이터 형태로 가지고 오겠다.

/ex07/src/main/java/com/example/controller/ProductController.java

package com.example.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.domain.ProductVO;
import com.example.mapper.ProductDAO;

@Controller
public class ProductController {

	@Autowired
	ProductDAO dao;
	
	@RequestMapping("/list.json")
	@ResponseBody
	public List<ProductVO> listJson(){
		return dao.list();
	}
}

 

데이터 출력 확인

 

 

- 페이지를 만들겠다.

/ex07/src/main/webapp/WEB-INF/views/list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<h1>[상품 목록]</h1>

 

- 이미지를 가지고 올 수 있도록 하겠다.

/ex07/src/main/java/com/example/controller/ProductController.java

package com.example.controller;

import java.io.File;
import java.io.FileInputStream;
import java.nio.file.Files;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.example.domain.ProductVO;
import com.example.mapper.ProductDAO;

@Controller
public class ProductController {
	@Autowired
	ProductDAO dao;

	// 파일 저장 루트 지정
	@Resource(name = "uploadPath")
	private String path;

	@RequestMapping("/list.json")
	@ResponseBody
	public List<ProductVO> listJson() {
		return dao.list();
	}

	// 이미지파일 브라우저에 출력
	@RequestMapping("/display")
	@ResponseBody
	public ResponseEntity<byte[]> display(String fileName) throws Exception {
		ResponseEntity<byte[]> result = null;
		// display fileName이 있는 경우
		if (!fileName.equals("")) {
			File file = new File(path + File.separator + fileName);
			HttpHeaders header = new HttpHeaders();
			header.add("Content-Type", Files.probeContentType(file.toPath()));
			result = new ResponseEntity<>(FileCopyUtils.copyToByteArray(file), header, HttpStatus.OK);
		}
		return result;
	}

}

 

확인

 

- 이제 탬플릿을 만들어서 목록을 출력해보도록 하겠다.

/ex07/src/main/webapp/WEB-INF/views/list.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<style>
#products {
	margin : 0px auto;
	width: 900px;
	overflow: hidden;
}

.box {
	width: 200px;
	height:200px;
	float: left;
	border: 1px solid rgb(224, 224, 235);
	margin: 5px;
	padding: 5px;
	box-shadow:5px 5px 5px gray;
}
.pname{
	text-overflow:ellipsis;
	white-space:nowrap;
	overflow: hidden;
}
</style>
<h1>[상품 목록]</h1>
<div id="products"></div>
<script id="temp" type="text/x-handlebars-template">
	{{#each .}}
	<div class="box">
		<img src="/display?fileName={{image}}" width=150/>
		<div class="pname">{{pname}}</div>
		<div class="price">{{price}}</div>
	</div>
	{{/each}}
</script>
<script>
	getList();
	function getList() {
		$.ajax({
			type : 'get',
			url : '/list.json',
			dataType : 'json',
			success : function(data) {
				var temp = Handlebars.compile($('#temp').html());
				$('#products').html(temp(data));
			}
		});
	}
</script>

 

확인

 

- 상품등록

- 컨트롤러 생성

/ex07/src/main/java/com/example/controller/ProductController.java

.....
	// 파일 저장 루트 지정
	@Resource(name = "uploadPath")
	private String path;

	@RequestMapping("/insert")
	public String insert(Model model) {
		model.addAttribute("pageName", "insert.jsp");
		return "home";
	}
.....

 

- 페이지 지정

/ex07/src/main/webapp/WEB-INF/views/home.jsp

.....
		<div id="header">
			<a href="/"><img src="/resources/back.png" width=960 /></a> // home으로
		</div>
		<div id="center">
			<div id="menu">
				<a href="/insert">상품등록</a> // insert로
			</div>
.....

 

- 페이지 생성

/ex07/src/main/webapp/WEB-INF/views/insert.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<style>
	form{
		width:930px;
	}
	input[type=text]{
		width:100%;
		margin-bottom: 10px;
		border:none;
		border-bottom: 1px solid gray;
		margin-bottom: 10px;
		font-size: 20px;
	}
</style>
<h1>[상품등록]</h1>
<form name="frm">
	<input type="text" name="pcode"/>
	<input type="text" name="pname" placeholder="상품명"/>
	<input type="text" name="price" placeholder="상품가격"/>
	<img src="http://placehold.it/300x250" id="image">
	<input type="file" name="image" style="display:none;"/>
	<hr/>
	<div>
		<input type="submit" value="상품등록"/>
		<input type="reset" value="등록취소"/>
	</div>
</form>

 

확인

 

- 이미지 변경하거나 삽입시

/ex07/src/main/webapp/WEB-INF/views/insert.jsp

.....
<h1>[상품등록]</h1>
<form name="frm">
	<input type="text" name="pcode"/>
	<input type="text" name="pname" placeholder="상품명"/>
	<input type="text" name="price" value="0"/>
	<img src="http://placehold.it/300x250" id="image" width=300>
	<input type="file" name="file" style="display:none"/>
	<hr/>
	<div>
		<input type="submit" value="상품등록"/>
		<input type="reset" value="등록취소"/>
	</div>
</form>
<script>
	// 이미지 tag 클릭시
	$('#image').on("click",function(){
		$(frm.file).click();
	})

	// 이미지를 삽입하거나 변경시
	$(frm.file).on("change",function(e){
		var file=$(this)[0].files[0];
		$("#image").attr("src", URL.createObjectURL(file));
	})
</script>

 

확인

 

- 상품 코드 가지고 오기

/ex07/src/main/resources/mapper/ProductMapper.xml

	<select id="maxCode" resultType="string">
		select max(pcode) from tbl_product
	</select>

 

/ex07/src/main/java/com/example/mapper/ProductDAO.java

	public String maxCode();
}

 

/ex07/src/main/java/com/example/mapper/ProductDAOImpl.java

	@Override
	public String maxCode() {
		return session.selectOne(namespace+".maxCode");
	}

}

 

/ex07/src/main/java/com/example/controller/ProductController.java

	// insert page
	@RequestMapping("/insert")
	public String insert(Model model) {
		// maxcode를 가지고 와서 최신 코드 입력
		String maxCode=dao.maxCode();
		String pcode = "P"+(Integer.parseInt(maxCode.substring(1))+1);
		model.addAttribute("pcode",pcode);
		model.addAttribute("pageName", "insert.jsp");
		return "home";
	}

 

/ex07/src/main/webapp/WEB-INF/views/insert.jsp

<form name="frm">
	<input type="text" name="pcode" value="${pcode}" readonly/>
	<input type="text" name="pname" placeholder="상품명"/>
	<input type="text" name="price" value="0"/>
	<img src="http://placehold.it/300x250" id="image" width=300>
	<input type="file" name="file" style="display:none"/>
	<hr/>
	<div>
		<input type="submit" value="상품등록"/>
		<input type="reset" value="등록취소"/>
	</div>
</form>

 

확인

 

- 이제 상품 등록 버튼을 통해서 상품등록을 하겠다.

/ex07/src/main/webapp/WEB-INF/views/insert.jsp

.....
<h1>[상품등록]</h1>
<form name="frm" enctype="multipart/form-data">
	<input type="text" name="pcode" value="${pcode}" readonly/>
	<input type="text" name="pname" placeholder="상품명"/>
	<input type="text" name="price" value="0"/>
	<img src="http://placehold.it/300x250" id="image" width=300>
	<input type="file" name="file" style="display:none"/>
	<hr/>
	<div>
		<input type="submit" value="상품등록"/>
		<input type="reset" value="등록취소"/>
	</div>
</form>
<script>
	// 상품등록
	$(frm).on('submit',function(e){
		e.preventDefault();
		var pname=$(frm.pname).val();
		//var price=$(frm.price).val();
		var file=$(frm.file).val();
		if(pname=="" || file==""){
			alert("상품이름과 상품이미지를 입력하세요.");
			return;
		};
		if(!confirm("상품을 등록하시겠습니까?")) return;
		frm.action="insert";
		frm.method="post";
		frm.submit();
	})
.....

 

/ex07/src/main/java/com/example/controller/ProductController.java

.....
	// insert page
	@RequestMapping("/insert")
	public String insert(Model model) {
		// maxcode를 가지고 와서 최신 코드 입력
		String maxCode=dao.maxCode();
		String pcode = "P"+(Integer.parseInt(maxCode.substring(1))+1);
		model.addAttribute("pcode",pcode);
		model.addAttribute("pageName", "insert.jsp");
		return "home";
	}
	// new
	// submit 작동후 이곳으로
	@RequestMapping(value = "/insert", method = RequestMethod.POST)
	public String insertPost(ProductVO vo, MultipartHttpServletRequest multi) throws Exception {
		MultipartFile file = multi.getFile("file"); // 업로드한 파일 지정
		// 파일 이름 유니크하게
		String image = System.currentTimeMillis() + "_" + file.getOriginalFilename();
		vo.setImage(image);
		// 파일 업로드 하기
		file.transferTo(new File(path + "/" + image));
		// 데이터 입력
		dao.insert(vo);
		return "redirect:/";
	}
.....

 

확인

 

 

- box를 클릭하여 상품정보를 보여주는 페이지를 만들어보겠다.

/ex07/src/main/java/com/example/controller/ProductController.java

.....
	// 파일 저장 루트 지정
	@Resource(name = "uploadPath")
	private String path;
	// new
	// read page
	@RequestMapping("/read")
	public String read(Model model, String pcode){
		model.addAttribute("vo",dao.read(pcode));
		model.addAttribute("pageName","read.jsp");
		return "home";
	}
.....

 

- list page에서 이미지 클릭시 상품 정보로 이동

/ex07/src/main/webapp/WEB-INF/views/list.jsp

.....
<script id="temp" type="text/x-handlebars-template">
	{{#each .}}
	<div class="box">
		<img src="/display?fileName={{image}}" width=150 
		onClick="location.href='read?pcode={{pcode}}'"/>
		<div class="pname">{{pname}}</div>
		<div class="price">{{price}}</div>
	</div>
	{{/each}}
</script>
.....

 

- read page

/ex07/src/main/webapp/WEB-INF/views/read.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<style>
#product {
	overflow: hidden;
}

.image {
	float: left;
	border: 1px solid rgb(224, 224, 235);
	box-shadow: 5px 5px 5px gray;
	margin: 10px;
	margin-right: 20px;
	padding: 10px;
}

.info {
	float: left;
	font-size: 20px;
	text-align: left;
}
</style>
<h1>[상품 정보]</h1>
<div>
	<div id="product">
		<div class="image">
			<img src="/display?fileName=${vo.image}" width=500 />
		</div>
		<div class="info">
			<p class="pcode">${vo.pcode }</p>
			<p class="pname">${vo.pname }</p>
			<p class="price">${vo.price }</p>
		</div>
	</div>
</div>

 

확인

 

- 첨부이미지를 미리보기 하겠다.

/ex07/src/main/webapp/WEB-INF/views/insert.jsp

.....
<h1>[상품등록]</h1>
<form name="frm" enctype="multipart/form-data">
	<input type="text" name="pcode" value="${pcode}" readonly/>
	<input type="text" name="pname" placeholder="상품명"/>
	<input type="text" name="price" value="0"/>
	<img src="http://placehold.it/300x250" id="image" width=300>
	<input type="file" name="file" style="display:none"/>
	<hr/>
	<div>첨부이미지 : <input type="file" name="files" acceept="image/*" multiple/></div>
	<div id="files"></div>
	<hr/>
	<div>
		<input type="submit" value="상품등록"/>
		<input type="reset" value="등록취소"/>
	</div>
</form>
<script>
	// 이미지 미리보기
	$(frm.files).on("change",function(){
		var files=$(this)[0].files;
		var str="";
		$.each(files,function(index,file){
			str+="<img src='"+URL.createObjectURL(file)+"'/>"
		});
		$("#files").html(str);
	});
.....

 

확인

 

- 첨부한 이미지를 저장한 테이블을 만들겠다.

- go to mysql

create table tbl_attach(
	image nvarchar(200) primary key not null,
    pcode char(4),
    foreign key(pcode) references tbl_product(pcode)
);

확인

 

- 상품등록할때 해당 테이블에 데이터를 넣어줄 수 있도록 하겠다.

/ex07/src/main/java/com/example/domain/ProductVO.java

.....
	// 멀티 이미지 
	private ArrayList<String> images;
    // getter setter tostring
.....

 

- 첨부 이미지가 콘솔에 우선 출력되는지 확인하겠다.

/ex07/src/main/java/com/example/controller/ProductController.java

.....
	// submit 작동후 이곳으로
	@RequestMapping(value = "/insert", method = RequestMethod.POST)
	public String insertPost(ProductVO vo, MultipartHttpServletRequest multi) throws Exception {
		MultipartFile file = multi.getFile("file"); // 업로드한 파일 지정
		// 파일 이름 유니크하게
		String image = System.currentTimeMillis() + "_" + file.getOriginalFilename();
		vo.setImage(image);
		// 대표 파일 업로드 하기
		file.transferTo(new File(path + "/" + image));
		// 데이터 입력
		dao.insert(vo);
		// new
		// 첨부 파일 업로드 하기
		List<MultipartFile> files = multi.getFiles("files");
		ArrayList<String> images=new ArrayList<String>();
		for(MultipartFile attFile:files){
			if(!attFile.isEmpty()){
				String attImage=System.currentTimeMillis()+"_"+attFile.getOriginalFilename();
				images.add(attImage);
			}
		}
		vo.setImages(images);
		System.out.println(vo.toString());
		return "redirect:/";
	}
.....

 

확인

 

- 업로드시 첨부파일 폴더가 없으면 생성하고 해당 폴더에 업로드하겠다.

/ex07/src/main/java/com/example/controller/ProductController.java

.....
		// 첨부 파일 업로드 하기
		List<MultipartFile> files = multi.getFiles("files");
		ArrayList<String> images=new ArrayList<String>();
		for(MultipartFile attFile:files){
			if(!attFile.isEmpty()){
				String attImage=System.currentTimeMillis()+"_"+attFile.getOriginalFilename();
				images.add(attImage);
				// 새폴더 생성
				File folder = new File(path+"/"+vo.getPcode());
				if(!folder.exists()){
					folder.mkdir();
				}
				// 해당 폴더 업로드
				attFile.transferTo(new File(path+"/"+vo.getPcode()+"/"+attImage));
			}
		}
.....

 

확인

 

 

- 이제 첨부파일용 mapper를 만들겠다.

/ex07/src/main/resources/mapper/AttachMapper.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="com.example.mapper.AttachMapper">
	<insert id="insert">
		insert into tbl_attach(pcode,image)
		values(#{pcode},#{image})
	</insert>
</mapper>

 

/ex07/src/main/java/com/example/mapper/AttachDAO.java

package com.example.mapper;

public interface AttachDAO {
	public void insert(String image, String pcode);
}

 

/ex07/src/main/java/com/example/mapper/AttachDAOImpl.java

package com.example.mapper;

import java.util.HashMap;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

@Repository
public class AttachDAOImpl implements AttachDAO {
	@Autowired
	SqlSession session;

	String namespace = "com.example.mapper.AttachMapper";

	@Override
	public void insert(String image, String pcode) {
		HashMap<String, Object> map = new HashMap<>();
		map.put("image", image);
		map.put("pcode", pcode);
		session.insert(namespace + ".insert", map);
	}

}

 

- 두가지 작업을 위한 서비스 생성

/ex07/src/main/java/com/example/service/ProductService.java

package com.example.service;

import com.example.domain.ProductVO;

public interface ProductService {
	public void insert(ProductVO vo);
}

 

/ex07/src/main/java/com/example/service/ProductServiceImpl.java

package com.example.service;

import java.util.ArrayList;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.domain.ProductVO;
import com.example.mapper.AttachDAO;
import com.example.mapper.ProductDAO;

@Service
public class ProductServiceImpl implements ProductService {
	@Autowired
	ProductDAO pdao;
	
	@Autowired
	AttachDAO adao;
	
	@Transactional
	@Override
	public void insert(ProductVO vo) {
		pdao.insert(vo);
		ArrayList<String> images=vo.getImages();
		for(String image:images){
			adao.insert(image, vo.getPcode());
		}
	}

}

 

/ex07/src/main/java/com/example/controller/ProductController.java

......
	@Autowired
	ProductService service;
......
	// submit 작동후 이곳으로
	@RequestMapping(value = "/insert", method = RequestMethod.POST)
	public String insertPost(ProductVO vo, MultipartHttpServletRequest multi) throws Exception {
		MultipartFile file = multi.getFile("file"); // 업로드한 파일 지정
		// 파일 이름 유니크하게
		String image = System.currentTimeMillis() + "_" + file.getOriginalFilename();
		vo.setImage(image);
		// 대표 파일 업로드 하기
		file.transferTo(new File(path + "/" + image));
		
		// 첨부 파일 업로드 하기
		List<MultipartFile> files = multi.getFiles("files");
		ArrayList<String> images=new ArrayList<String>();
		for(MultipartFile attFile:files){
			if(!attFile.isEmpty()){
				String attImage=System.currentTimeMillis()+"_"+attFile.getOriginalFilename();
				images.add(attImage);
				// 새폴더 생성
				File folder = new File(path+"/"+vo.getPcode());
				if(!folder.exists()){
					folder.mkdir();
				}
				// 해당 폴더 업로드
				attFile.transferTo(new File(path+"/"+vo.getPcode()+"/"+attImage));
			}
		}
		vo.setImages(images);
		// System.out.println(vo.toString());
		
        // new
		// 데이터 입력
		service.insert(vo);
		return "redirect:/";
	}

 

확인

 

 

- 이제 첨부 이미지가 상품 정보 페이지에 출력될 수 있도록 하겠다.

/ex07/src/main/resources/mapper/AttachMapper.xml

.....
	<select id="list" resultType="string">
		select image from tbl_attach
		where pcode=#{pcode}
	</select>
</mapper>

 

/ex07/src/main/java/com/example/mapper/AttachDAO.java

.....
	public List<String> list(String pcode);
}

 

/ex07/src/main/java/com/example/mapper/AttachDAOImpl.java

.....
	@Override
	public List<String> list(String pcode) {
		return session.selectList(namespace+".list",pcode);
	}

}

 

- test

/ex07/src/test/java/com/example/controller/MysqlTest.java

package com.example.controller;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.example.domain.ProductVO;
import com.example.mapper.AttachDAO;
import com.example.mapper.MysqlMapper;
import com.example.mapper.ProductDAO;

@RunWith(SpringJUnit4ClassRunner.class) // 먼저 SpringJUnit4ClassRunner.class
										// import한다.
@ContextConfiguration(locations={"file:src/main/webapp/WEB-INF/spring/**/*.xml"})
public class MysqlTest {
	// 자동으로 연결
	@Autowired
	private ProductDAO dao;

	@Autowired
	AttachDAO adao;
	
	// 연결해서 insert을 테스트
	@Test
	public void insert() {
		adao.list("P124");
	}
}

 

확인

 

- 컨트롤러에서 read가기전에 데이터를 담아서 넘기겠다.

/ex07/src/main/java/com/example/controller/ProductController.java

.....
	@Autowired // new
	AttachDAO adao;
	
	// 파일 저장 루트 지정
	@Resource(name = "uploadPath")
	private String path;

	// read page
	@RequestMapping("/read")
	public String read(Model model, String pcode){
		model.addAttribute("attList", adao.list(pcode)); // new
		model.addAttribute("vo",dao.read(pcode));
		model.addAttribute("pageName","read.jsp");
		return "home";
	}
......

 

- 첨부 파일 업로드시 Path까지 넣어주도록 하겠다.

/ex07/src/main/java/com/example/controller/ProductController.java

....
// 첨부 파일 업로드 하기
		List<MultipartFile> files = multi.getFiles("files");
		ArrayList<String> images=new ArrayList<String>();
		for(MultipartFile attFile:files){
			if(!attFile.isEmpty()){
				String attImage=System.currentTimeMillis()+"_"+attFile.getOriginalFilename();
				images.add(vo.getPcode()+"/"+attImage); //// new new
				// 새폴더 생성
				File folder = new File(path+"/"+vo.getPcode());
				if(!folder.exists()){
					folder.mkdir();
				}
				// 해당 폴더 업로드
				attFile.transferTo(new File(path+"/"+vo.getPcode()+"/"+attImage));
			}
		}
		vo.setImages(images);
		// System.out.println(vo.toString());
		
		// 데이터 입력
		service.insert(vo);
		return "redirect:/";
	}
....

 

/ex07/src/main/webapp/WEB-INF/views/read.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<style>
#product {
	overflow: hidden;
}

.image {
	float: left;
	border: 1px solid rgb(224, 224, 235);
	box-shadow: 5px 5px 5px gray;
	margin: 10px;
	margin-right: 20px;
	padding: 10px;
}

.info {
	float: left;
	font-size: 20px;
	text-align: left;
}
</style>
<h1>[상품 정보]</h1>
<div>
	<div id="product">
		<div class="image">
			<img src="/display?fileName=${vo.image}" width=500 />
		</div>
		<div class="info">
			<p class="pcode">${vo.pcode }</p>
			<p class="pname">${vo.pname }</p>
			<p class="price">${vo.price }</p>
		</div>
	</div>
	<hr />
	<div id="products">
		<c:forEach items="${attList}" var="image">
			<img src="/display?fileName=${image}" width=150 />
		</c:forEach>
	</div>
</div>

 

확인

 

 

- 게시글을 삭제해보겠다.

	<delete id="delete">
		delete from tbl_attach
		where image=#{image}
	</delete>
</mapper>

 

/ex07/src/main/java/com/example/mapper/AttachDAO.java

	public void delete(String image);
}

 

/ex07/src/main/java/com/example/mapper/AttachDAOImpl.java

	@Override
	public void delete(String image) {
		session.delete(namespace + ".delete", image);
	}

}

 

/ex07/src/main/java/com/example/controller/ProductController.java

.....
	// 파일 저장 루트 지정
	@Resource(name = "uploadPath")
	private String path;
	// new
	// 첨부파일 삭제
	@RequestMapping("/attDelete")
	@ResponseBody
	public void attDelete(String image){
		adao.delete(image); // 테이블 삭제
		new File(path+"/"+image).delete(); // 디스크에서 삭제
	}
.....

 

/ex07/src/main/webapp/WEB-INF/views/read.jsp

.....
<div id="products">
		<c:forEach items="${attList}" var="image">
			<div>
				<img src="/display?fileName=${image}" width=150 />
				<a href="${image}">삭제</a>
			</div>
		</c:forEach>
	</div>
</div>

<script>
	$("#products").on("click","a",function(e){
		e.preventDefault();
		var img=$(this).parent();
		var image=$(this).attr("href");
		if(!confirm("해당 파일을 삭제하시겠습니까?")) return
		$.ajax({
			type:'get',
			url:"/attDelete",
			data:{"image":image},
			success:function(){
				alert("첨부파일 삭제 성공");
				img.remove();
			}
		})
	})
</script>

 

확인

 

 

- 제품 상세 페이지에서 이미지를 업로드 하도록 하겠다.

/ex07/src/main/webapp/WEB-INF/views/read.jsp

....
	<div id="products">
		<c:forEach items="${attList}" var="image">
			<div>
				<img src="/display?fileName=${image}" width=150 /> <a
					href="${image}">삭제</a>
			</div>
		</c:forEach>
	</div>
</div>

<script>
	var pcode = "${vo.pcode}";
	   $("#file").on("change", function(e){
	      var file=$(this)[0].files[0];
	      if(file==null) return;
	      var formData = new FormData();
	      
	      formData.append("file", file);
	      formData.append("pcode", pcode);
	      $.ajax({
	         type:"post",
	         url: "/attInsert",
	         data: formData,
	         processData:false,
	         contentType: false,
	         success: function(data){
	            var str="<div style='float:left;'>";
	            str += "<img src='/display?fileName=" + data + "' width=200/>";
	            str += "<a href='" + data + "'>삭제</a>";
	            str += "</div>";
	            $("#products").append(str);
	         }
	      });
	   });
....

 

/ex07/src/main/java/com/example/controller/ProductController.java

.....
	// 파일 저장 루트 지정
	@Resource(name = "uploadPath")
	private String path;

	// 첨부파일 입력
	@RequestMapping("/attInsert")
	@ResponseBody
	public String attInsert(String pcode, MultipartFile file) throws Exception {
		File folder = new File(path + "/" + pcode);
		if (!folder.exists()) {
			folder.mkdir();
		}
		String image = pcode + "/" + System.currentTimeMillis() + "_" + file.getOriginalFilename();
		file.transferTo(new File(path + "/" + image));
		adao.insert(image, pcode);
		return image;
	}
.....