JAVA/JSP

[JSP] 구현 4 - 이미지 추가

아잠만_ 2024. 7. 5. 16:44

구현

구현2 - 상세보기 구현

구현3 - 상품 추가

목표

products.jsp와 product.jsp에 해당 사진이 보여지도록하고
addProduct.jsp에서 사진을 저장하도록 한다

ProductVO추가 (filename 저장 프로퍼티 생성)

package kr.or.ddit.vo;

// 자바 빈 클래스
/*  자바빈 규약
  	1. 프로퍼티
  	2. 기본생성자
  	3. getter/setter메서드
 */
/**
 * @author PC-13
 *
 */
public class ProductVO {
	// 프로퍼티 = 멤버변수(필드)
	private String productId;
	private String pname;
	private long unitPrice;
	private String description;
	private String manufacturer;
	private String category;
	private long unitsInStock;
	private String condition;
	private String filename;
	
	// 기본 생성자
	public ProductVO() {
	}
	
	// 생성자(상품아이디, 상품명, 상품가격)
	public ProductVO(String productId, String pname, long unitPrice) {
		this.productId = productId;
		this.pname = pname;
		this.unitPrice = unitPrice;
	}

	// getter / setter 메서드
	public String getProductId() {
		return productId;
	}

	public void setProductId(String productId) {
		this.productId = productId;
	}

	public String getPname() {
		return pname;
	}

	public void setPname(String pname) {
		this.pname = pname;
	}

	public long getUnitPrice() {
		return unitPrice;
	}

	public void setUnitPrice(long unitPrice) {
		this.unitPrice = unitPrice;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getManufacturer() {
		return manufacturer;
	}

	public void setManufacturer(String manufacturer) {
		this.manufacturer = manufacturer;
	}

	public String getCategory() {
		return category;
	}

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

	public long getUnitsInStock() {
		return unitsInStock;
	}

	public void setUnitsInStock(long unitsInStock) {
		this.unitsInStock = unitsInStock;
	}

	public String getCondition() {
		return condition;
	}

	public void setCondition(String condition) {
		this.condition = condition;
	}
	

	public String getFilename() {
		return filename;
	}

	public void setFilename(String filename) {
		this.filename = filename;
	}

	@Override
	public String toString() {
		return "ProductVO [productId=" + productId + ", pname=" + pname + ", unitPrice=" + unitPrice + ", description="
				+ description + ", manufacturer=" + manufacturer + ", category=" + category + ", unitsInStock="
				+ unitsInStock + ", condition=" + condition + "]";
	}

	
}

Dao 수정

(기존 파일 사진 추가)

package kr.or.ddit.dao;

import java.util.ArrayList;
import java.util.List;

import kr.or.ddit.vo.ProductVO;

public class ProductRepository {
	// 싱글톤 객체
	private static ProductRepository dao;
	
	public static ProductRepository getInstance() {
		if(dao==null) dao = new ProductRepository();
		return dao;
	}
	
	private List<ProductVO> listOfProducts = new ArrayList<ProductVO>();
	
	// 기본 생성자 3개의 상품 정보를 설정
	private ProductRepository() {
		ProductVO phone = new ProductVO("P1234", "iPhone 15s", 1250000);
		phone.setDescription("iPhone 15 · A16 Bionic 칩 5코어 GPU 탑재 · 첨단 듀얼 카메라 시스템. 48MP 메인 카메라 2배 망원 지원 울트라 와이드 카메라 · 최대 26시간 동영상 재생 ");
		phone.setCategory("Smart Phone");
		phone.setManufacturer("Apple");
		phone.setUnitsInStock(1000);
		phone.setCondition("New");
		phone.setFilename("P1234.jpg");
		
		ProductVO notebook = new ProductVO("P1235", "LG Gram", 2000000);
		notebook.setDescription("최대 22시간의 배터리 성능이 선사하는 궁극의 프로급 휴대성");
		notebook.setCategory("NoteBook");
		notebook.setManufacturer("LG");
		notebook.setUnitsInStock(1000);
		notebook.setCondition("Old");
		notebook.setFilename("P1235.jpg");
		
		ProductVO tablet = new ProductVO("P1236", "갤럭시 탭 S9 Ultra", 1700000);
		tablet.setDescription("디스플레이 369.9 mm 다이나믹 아몰레드 2X · 내구성 IP68 · 프로세서 스냅드래곤8 2세대");
		tablet.setCategory("Tablet");
		tablet.setManufacturer("Samsung");
		tablet.setUnitsInStock(1000);
		tablet.setCondition("Refurbished");
		tablet.setFilename("P1236.jpg");
		
		listOfProducts.add(phone);
		listOfProducts.add(notebook);
		listOfProducts.add(tablet);
	}
	
	// ProductVO 객체 타입의 변수 listOfProducts에 저장된 모든 상품 목록을 가져옴
	public List<ProductVO> getAllProducts(){
		return listOfProducts;
	}
	
	// 상품 상세 보기
	// listOfProducts 변수에 저장된 3개 (이상)의 상품 목록 중
	// 선택한 상품의 아이디와 일치하는 상품 정보를 가져옴
	public ProductVO getProductById(String productId) {
		for(ProductVO vo : listOfProducts) {
			String id = vo.getProductId();
			if(id.equals(productId)) {
				return vo;
			}
		}
		return null;
	};
	
	// 상품 등록(무엇을 어디에 추가)
	public void addProduct(ProductVO vo) {
		listOfProducts.add(vo);
	};
}

Products.jsp 수정

사진 추가

<%@page import="kr.or.ddit.vo.ProductVO"%>
<%@page import="java.util.List"%>
<%@page import="kr.or.ddit.dao.ProductRepository"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<title>상품 목록</title>
<link rel="stylesheet" href="/css/bootstrap.min.css">
<script src="/js/jquery-3.6.0.js"></script>
<script>
$(function(){
	$(document).on('click','.col-md-4',function(){
		let id = $(this).attr("name");
		location.href="/product.jsp?pid="+id;
	})
})
</script>
</head>
<body>
	<!-- header -->
	<!-- 
	디렉티브 태그 	속성 	    속성의 값		 -->
	<%@ include file="menu.jsp" %>
	<% // 스크립틀릿
	// 클래스			    객체 	 클래스			     메서드(호출)
	ProductRepository dao = ProductRepository.getInstance();
	// 객체타입		변수		객체	메서드(호출)
	List<ProductVO> list = dao.getAllProducts();
	%>
	<!-- 상품 목록 시작 -->
	<div class="jumbotron">
		<div class="container">
			<h1 class="display-3">Product List</h1>
		</div>
	</div>
	<div class="container">
		<div class="row" align="center">
		<c:set var="list" value="<%=list %>"/>
		<!-- 상품 반복 부분 시작 -->
		
		<!-- 객체 타입 변수 list에 저장된 상품 목록 개수만큼 실행하는 JSTL 반복문 -->
		<c:forEach var="vo" items="${list}">
			<div class="col-md-4" name="${vo.productId}">
				<!-- EL 표현문 -->
				<h3>${vo.pname}</h3>
				<img src="/images/${vo.filename}" style='width:200px;' />
				<p>${vo.description}</p>
				<p><fmt:formatNumber value="${vo.unitPrice}" type="number" pattern="#,###" />원</p>
				<p>
					<a href="product.jsp?pid=${vo.productId}" class="btn btn-secondary" role="button">상세정보 &raquo;</a>
				</p>
			</div>
		</c:forEach>		
		<!-- 상품 반복 부분 끝 -->
		</div>
		<hr />
			<div class="form-group row">
				<div class="col-sm-offset-2 col-sm-10">
					<a href="/addProduct.jsp" class="btn btn-primary" role="button">등록</a>
				</div>
			</div>
	</div>
	<!-- 상품 목록 끝 -->
	
	<!-- footer -->
	<%@ include file="footer.jsp" %>	
</body>
</html>

Product.jsp 수정

사진 추가

<%@page import="kr.or.ddit.vo.ProductVO"%>
<%@page import="java.util.List"%>
<%@page import="kr.or.ddit.dao.ProductRepository"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<!DOCTYPE html>
<html>
<head>
<title>상품 목록</title>
<link rel="stylesheet" href="/css/bootstrap.min.css">
</head>
<body>
	<!-- header -->
	<!-- 
	디렉티브 태그 	속성 	    속성의 값		 -->
	<%@ include file="menu.jsp" %>
	<!-- 상품 목록 시작 -->
	<div class="jumbotron">
		<div class="container">
			<h1 class="display-3">상품 상세</h1>
		</div>
	</div>
	<div class="container">
		<%
			String pid = request.getParameter("pid");
			ProductRepository dao = ProductRepository.getInstance();
			ProductVO vo = dao.getProductById(pid);
		%>
		<c:set var="vo" value="<%=vo %>"/>
		<div class="row">
			<div class="col-md-6"> <!-- style = " width : 50%; "-->
				<span class="badge badge-danger">${vo.condition}</span>
				<h3>
				${vo.pname}</h3>
				<img src="/images/${vo.filename}" style='width:200px;' />
				<p>${vo.description}</p>
				<p>
					<b>상품 코드 : </b>
					${vo.productId}
				</p>
				<p>
					<b>제조사 : </b>
					${vo.manufacturer}
				</p>
				<p>
					<b>분류 : </b>
					${vo.category}
				</p>
				<p>
					<b>재고 수 : </b>
					${vo.unitsInStock}
				</p>
				<h4>
					<fmt:formatNumber value="${vo.unitPrice}" type="number" pattern="#,###" />원
				</h4>
				<p>
					<a href="/products.jsp" class="btn btn-secondary" role="button">상품 목록 &raquo;</a>
				</p>
			</div>
		</div>
	</div>
	<!-- 상품 목록 끝 -->
	
	<!-- footer -->
	<%@ include file="footer.jsp" %>	
</body>
</html>

addProduct.jsp

파일 input추가와 form에 enctype="multipart/form-data"추가

<%@page import="kr.or.ddit.vo.ProductVO"%>
<%@page import="java.util.List"%>
<%@page import="kr.or.ddit.dao.ProductRepository"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/css/bootstrap.min.css">
<script src="/js/jquery.min.js"></script>
<title>상품 목록</title>
<script>
$(function(){
	$('input:file').on('input',handleImg);
})

// e 이벤트 객체
function handleImg(e){
	let files = e.target.files; // 파일에 접근
	let fileArr = Array.prototype.slice.call(files); // 붙어있는 파일을 배열로 정렬
	$.each(fileArr, function(){
		// 이미지 여부 체크
		if(!this.type.match("image.*")){
			console.log("이미지가 아닙니다")
			return;
		}
		
		// 이미지 
		let reader = new FileReader();
		
		// e : reader가 이미지 객체를 읽는 이벤트
		reader.onload = function(e){
			let img_html = "<img src='"+e.target.result+"' style='width:200px;' />";
			// 요소.append : 누적, 요소.html : 새로고침,
			// 요소.innerHTML : JavaScript에서 새로고침
			$('#pImg').html(img_html);
		}
		reader.readAsDataURL(this);
	});
}
</script>
</head>
<body>
	<!-- header -->
	<!-- 
	디렉티브 태그 	속성 	    속성의 값		 -->
	<%@ include file="menu.jsp"%>
	<!-- 상품 목록 시작 -->
	<div class="jumbotron">
		<div class="container">
			<h1 class="display-3">상품 등록</h1>
		</div>
	</div>
	<div class="container">
	<form action="processAddProduct.jsp" method="post" enctype="multipart/form-data" class="form-horizontal" name="newProduct">
		<div class="form-group row">
			<label class="col-sm-2">상품 코드</label>
			<div class="col-sm-3">
				<input type="text" name="productId">
			</div>
		</div>
		<div class="form-group row">
			<label class="col-sm-2">상품명</label>
			<div class="col-sm-3">
				<input type="text" name="pname">
			</div>
		</div>
		<div class="form-group row">
			<label class="col-sm-2">가격</label>
			<div class="col-sm-3">
				<input type="text" name="unitPrice">
			</div>
		</div>
		<div class="form-group row">
			<label class="col-sm-2">상세정보</label>
			<div class="col-sm-5">
				<textarea rows="2" cols="23" name="description">
						
				</textarea>
			</div>
		</div>
		<div class="form-group row">
			<label class="col-sm-2">제조사</label>
			<div class="col-sm-3">
				<input type="text" name="manufacturer">
			</div>
		</div>
		<div class="form-group row">
			<label class="col-sm-2">카테고리</label>
			<div class="col-sm-3">
				<input type="text" name="category">
			</div>
		</div>
		<div class="form-group row">
			<label class="col-sm-2">재고수</label>
			<div class="col-sm-3">
				<input type="text" name="unitsInStock">
			</div>
		</div>
		<div class="form-group row">
			<label class="col-sm-2">상태</label>
			<div class="col-sm-5">
				<input type="radio" id="new" name="condition" value="New">
				<label for="new">신규 제품</label>
				<input type="radio" id="old" name="condition" value="Old">
				<label for="old">중고 제품</label>
				<input type="radio" id="Refurbished" name="condition" value="Refurbished">
				<label for="Refurbished">재생 제품</label>
			</div>
		</div>
		<div class="form-group row">
			<label class="col-sm-2">이미지</label>
			<div class="col-sm-3">
				<input type="file" name="filename">
				<span id="pImg"></span>
			</div>
		</div>
		<div class="form-group row">
			<div class="col-sm-offset-2 col-sm-10">
				<input class="btn btn-primary" type="submit" value="등록" />
			</div>
		</div>
		<hr />
	</form>
	</div>
	<!-- 상품 목록 끝 -->

	<!-- footer -->
	<%@ include file="footer.jsp"%>
</body>
</html>

processAddProduct.jsp 수정

파일여부를 확인해 파일일 때는 저장하고 vo객체에 이름 값 저장하고

파일이 아닐때는 값을 각각의 vo값에 저장하여 insert작업을 수행한다

<%@page import="org.apache.commons.fileupload.DiskFileUpload"%>
<%@page import="java.io.File"%>
<%@page import="java.util.UUID"%>
<%@page import="org.apache.commons.fileupload.FileItem"%>
<%@page import="java.util.Iterator"%>
<%@page import="java.util.List"%>
<%@page import="kr.or.ddit.dao.ProductRepository"%>
<%@page import="kr.or.ddit.vo.ProductVO"%>
<%@page import="java.util.Enumeration"%>
<%@ page language="java" contentType="text/html; charset=UTF-8"%>
<%
	request.setCharacterEncoding("utf-8");
	
//윈도우 경로 : 역슬러시 두 개
// 해당 경로는 가져오는 과정이 있기 때문에  redirect로 페이지를 이동했을 때 해당 이미지가 바로 표시되지 않는다 cash폴더를 이용해야 빠르게 접근이 가능하다
// 	String path = "D:\\A_TeachingMaterial\\06_JSP\\workspace\\JSPBook\\WebContent\\images";
	// 캐시파일로 저장할 경우 바로 이미지가 나옴 org.eclipse.wst.server.core\\tmp0\\wtpwebapps 의 경로로 들어갈 것
	String path = "D:\\A_TeachingMaterial\\06_JSP\\workspace\\.metadata\\.plugins\\org.eclipse.wst.server.core\\tmp0\\wtpwebapps\\JSPBook\\images";
	
	//commons-fileupload.jar 안에 해당 클래스가 있음
	DiskFileUpload upload = new DiskFileUpload();
	
	// 업로드 할 파일 크기의 최대 크기
	upload.setSizeMax(5000000); //5Mbyte
	
	// 메모리에 저장할 최대 크기
	upload.setSizeThreshold(5*4096); // 5 * 1024 * 1024
	
	// 업로드할 파일을 임시로 저장할 경로(폴더가 없으면 자동으로 폴더가 생성됨 mkdir()으로 생성하지 않아도됨)
	upload.setRepositoryPath(path);
	
	// 요청 파라미터 : {filename=파일객체}
	// parse : 구문분석(오류체크), 의미분석, 변환
	List items = upload.parseRequest(request);
	
	// 요청 파라미터 들을 Iterator(열거) 클래스로 변환
	Iterator params = items.iterator();
	
	ProductVO vo = new ProductVO();
	
	// 요청 파라미터가 없어질 때 까지 반복
	while(params.hasNext()){
		// FileItem : 일반데이터(text, radio, checkbox)
		//            파일(file)
		FileItem item = (FileItem) params.next();
	
		//isFormField() > true > 일반데이터
		if(!item.isFormField()){// 파일(input type="file")
			// 요청 파라미터 : {filename=파일객체} => item
			
			String fileFieldName = item.getFieldName(); // filename
			
			String fileName = item.getName(); // 업로드 될 파일 명(경로 포함)
			fileName = fileName.substring(fileName.lastIndexOf("\\")+1); // 경로 삭제
			
			// 이미지파일이라면 MIME TYPE : image/jpg
			String contentType = item.getContentType(); 
			
			long fileSize = item.getSize(); //파일의 크기
			
			// 파일이 이미 있으면 already exists: 오류 발생
			//파일명 중복 방지 시작
			UUID uuid = UUID.randomUUID();	
			fileName = uuid.toString() + "_" +fileName;
			
			// c:\\upload\\사진.jpg로 복사
			File file = new File(path, fileName);
			
			// 복사 실행
			item.write(file);
			vo.setFilename(fileName);

		} else{
			String key = item.getFieldName();
			String value = item.getString("UTF-8"); // 한글 처리
			// if문으로 비교해서 넣기..
			if(key.equals("productId")){
				vo.setProductId(value);
			} else if(key.equals("pname")){
				vo.setPname(value);
			} else if(key.equals("unitPrice")){
				vo.setUnitPrice(Long.parseLong(value));
			} else if(key.equals("description")){
				vo.setDescription(value);
			} else if(key.equals("manufacturer")){
				vo.setManufacturer(value);
			} else if(key.equals("category")){
				vo.setCategory(value);
			} else if(key.equals("unitsInStock")){
				vo.setUnitsInStock(Integer.parseInt(value));
			} else if(key.equals("condition")){
				vo.setCondition(value);
			} 
		}
	}

	ProductRepository dao = ProductRepository.getInstance();
	// 새로운 상품 등록
	dao.addProduct(vo);
	// 목록이동 (redirect : url)
	response.sendRedirect("/products.jsp");
	
%>