Servlet, JSP/Servlet, JSP

23-03-15 Servlet, JSP

모건이삼촌 2023. 3. 15. 16:31

※ 복습

 

 

 

 

※ 수업

 

게시글 상세조회시 첨부파일을 확인할 수 있어야함

삭제시에도 첨부파일도 삭제되어야함

db에서도 삭제되어야함

 

1. 썸네일이 안만들어지는 이미지 처리

 1-1 . fileUploadController 수정

				// 이미지 여부 확인
				// image/x-icon, image/webp
				List<String> exceptImgMime = Arrays.asList("image/x-icon", "image/webp");
				boolean image = p.getContentType().startsWith("image") && exceptImgMime.contains(p.getContentType());

 1-1 2번째방법

// 이미지 여부 확인
// image/x-icon, image/webp
System.out.println(p.getContentType());
//List<String> exceptImgMimes = Arrays.asList("image/x-icon", "image/webp");
boolean image = p.getContentType().startsWith("image");  // && !exceptImgMimes.contains(p.getContentType());
				
if(image) {
// 썸네일 생성
try {
File out = new File(targetPath, uuid + "_t" + ext);
Thumbnailator.createThumbnail(fs, out, 200, 200);
}
catch (UnsupportedFormatException ignore) {}

2. 댓글수를 확인할수있는 db구문 추가

select tb.*, (select count(*) from tbl_reply tr where tr.bno = tb.bno) replyCnt
from tbl_board tb
where category = 1
and ( title like '%man%' or content like '%man%' or writer like '%man%' )
order by bno desc limit 10 offset 0;

2-1 BoardDao

 selectOne, selectList 쿼리 수정

		public Board selectOne(Long bno) {
			conn = DBConn.getConnection();
			// 반환 예정 객체
			Board board = null;
			// 처리할 sql구문
			String sql = "select tb.*, (select count(*) from tbl_reply tr where tr.bno = tb.bno) replyCnt\r\n"
					+ "from tbl_board tb where bno = ?";
                    
public List<Board> selectList(Criteria cri) {
			conn = DBConn.getConnection();
			// 반환 예정 객체
			List<Board> boards = new ArrayList<Board>();
			// 처리할 sql구문
			String sql =""; 
			sql += "select tb.*, (select count(*) from tbl_reply tr where tr.bno = tb.bno) replyCnt\r\n"
					+ "from tbl_board tb where category = ?";

list jsp수정

        <c:forEach var="board" items="${boards}" varStatus="stat">
          <a href="view?bno=${board.bno}&${page.cri.fullQueryString}" class="text-decoration-none">
            <div class="container card-body ${stat.last ? '': 'border-bottom'}">
              <div class="mb-4 text-truncate">${board.title} <span class="small text-muted">[${board.replyCnt}]</span></div>
              <div class="row text-muted small">
                <div class="col-7">${board.writer}</div>
                <div class="col text-end small"><span class="mx-3">${board.regdate}</span> <span>조회수 ${board.hitcount}</span></div>
              </div>
            </div>
          </a>
          </c:forEach>

 

* 게시글 이름이 null일경우 게시판들어갈시 nullPointException(500에러)이 나올때

1. db를 통해 게시글이름이 null인 게시글을 찾는다

2. null이 있는경우 delete구문을 통해 게시글을 삭제한다

3. 해결완료

 

게시글 작성시 input type file을 추가할건데 가지고있는 첨부파일이 있따면 모두 인식을 할것임

 

3. 파일첨부 

3-1. paramsolver 수정

package com.chanyongyang.jsp.util;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;

import com.chanyongyang.jsp.domain.Attach;
import com.chanyongyang.jsp.domain.Board;
import com.chanyongyang.jsp.domain.Criteria;
import com.chanyongyang.jsp.domain.Member;

import net.coobird.thumbnailator.Thumbnailator;
import net.coobird.thumbnailator.tasks.UnsupportedFormatException;

public class ParamSolver {
	public static final String UPLOAD_PATH = "c:/upload";
	
	// T는 클래스타입, clazz의 Class<T> 타입으로 만들어줌
	public static <T> T getParams(HttpServletRequest req, Class<T> clazz) {

		// 인스턴스 생성
		T t = null;
		try {
			t = clazz.getDeclaredConstructor().newInstance();
			
			// 선언 필드에 대한 타입 및 이름 체크
			Field[] fields = clazz.getDeclaredFields();
			for(Field f : fields) {
				String fieldName = f.getName();
				String setterName = "set" + f.getName().substring(0, 1).toUpperCase() + fieldName.substring(1);
				Method[] methods = clazz.getDeclaredMethods();
				for(Method m : methods) {
					if(setterName.equals(m.getName())) {
						if(req.getParameter(fieldName) == null) {
							continue;
						}
						if(f.getType() == Integer.class || f.getType() == int.class) {
							m.invoke(t, Integer.parseInt(req.getParameter(fieldName)));
						}
						if(f.getType() == String.class) {
							m.invoke(t, req.getParameter(fieldName));
						}
						if(f.getType() == String[].class) {
							m.invoke(t, (Object)req.getParameterValues(fieldName));
						}
						if(f.getType() == Long.class || f.getType() == long.class) {
							m.invoke(t, Long.valueOf(req.getParameter(fieldName)));
						}
					}
				}
			}
			if(req.getContentType() == null || !req.getContentType().startsWith("multipart")) {
				return t;
			}
			
			Collection<Part> parts = req.getParts();
			
			List<Attach> attachs = new ArrayList<Attach>();
			for(Part p : parts) {
				if(p.getContentType() == null) {
					continue;
				}
				// 파일의 원본이름
				String origin = p.getSubmittedFileName();

				// 파일명 중 마지막 .의 위치
				int dotIdx = origin.lastIndexOf(".");

				// 확장자를 담을 변수
				String ext = "";

				// 확장자 구하기
				if (dotIdx > -1) {
					ext = origin.substring(dotIdx); // 확장자명이 없는경우 처리
				}

				// UUID 문자열 생성
				String uuid = UUID.randomUUID().toString();

				// thumbnail
				// 경로 문자열 반환
				String path = getTodayStr();

				// 경로 문자열에 대한 폴더 생성
				File targetPath = new File(ParamSolver.UPLOAD_PATH, path);
				if (!targetPath.exists()) {
					targetPath.mkdirs();
				}

				// 원본에 대한 저장
				File fs = new File(targetPath, uuid + ext);
				p.write(fs.getPath());

				// 이미지 여부 확인
				// image/x-icon, image/webp
//				System.out.println(p.getContentType());
//					List<String> exceptImgMimes = Arrays.asList("image/x-icon", "image/webp");
				boolean image = p.getContentType().startsWith("image"); // &&
																		// !exceptImgMimes.contains(p.getContentType());

				if (image) {
					// 썸네일 생성
					try {
						File out = new File(targetPath, uuid + "_t" + ext);
						Thumbnailator.createThumbnail(fs, out, 200, 200);
					} 
					catch (UnsupportedFormatException ignore) {}
				}
				attachs.add(new Attach(uuid, origin, image, path));
				// uuid, origin, image, path
//				attachs.forEach(System.out::println);
			}
			if(clazz == Board.class) {
				((Board)t).setAttachs(attachs);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return t;
	}
	
	// 로그인 여부 확인
	public static boolean isLogin(HttpServletRequest req) {
		return req.getSession().getAttribute("member") != null;
	}
	
	// 본인인지 확인
	public static boolean isMine(HttpServletRequest req, String writer) {
		return isLogin(req) && ((Member)req.getSession().getAttribute("member")).getId().equals(writer);
	}

	private static String getTodayStr() {
		return new SimpleDateFormat("yyyy/MM/dd").format(System.currentTimeMillis());
	}
	public static void main(String[] args) {
		getParams(null, Criteria.class);
	}
}

 

3-2 view.jsp 파일첨부 부분 추가

  			<div class="mb-3">
    			<label for="writer" class="form-label">writer</label>
    			<input type="text" class="form-control" id="writer" placeholder="Enter writer" name="writer" value="${member.id}" readonly>
  			</div>
  			<div class="mb-3 mt-3">
  				<label for="file" class="form-label">files</label>
   				<input type="file" class="form-control" id="file" name="file" multiple>
 			</div>

 

board, reply, member는 table도 있고 vo도 있다.

첨부파일같은경우는 위치상 애매하고 일대다의 형태를 가지고잇음

일대다 같은경우는 table이 필요함 어느글에 대한 게시글인지 알아야함

BoardDao insert 구문 수정

		public Long insert(Board board) {
			conn = DBConn.getConnection();
			long result = 0;
			// 처리할 sql구문, 지정타입에 맞춰서 ? 로 바꿀수 있음 
			String sql = "insert into tbl_board (title, content, writer) values (?, ?, ?)";
			try {
				// 문장 생성
				pstmt = conn.prepareStatement(sql);
				pstmt.setString(1, board.getTitle());
				pstmt.setString(2, board.getContent());
				pstmt.setString(3, board.getWriter());
				
				// 문장 처리
				pstmt.executeUpdate();
				close();
				// 쿼리 재실행
				// 작성한 게시글의 글번호 조회 , insert가 끝난시점에서 함
				sql = "select max(bno) from tbl_board";
				conn = DBConn.getConnection();
				pstmt = conn.prepareStatement(sql);
				rs = pstmt.executeQuery();
				rs.next(); // 커서 이동(행간이동) / 조건식을 넣지 않은이유는 insert후에 실행되서 작성된글이 있다는걸 알기 때문 
				result = rs.getLong(1); // 1번컬럼을 long타입으로 반환
				close();
				
			} catch (SQLException e) {
				e.printStackTrace();
			}
			return result;
		}

수정 후 실행해서 글 작성 후 글번호 확인

 

 

AttachVo 생성

package com.chanyongyang.jsp.domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Attach {
	private String uuid;
	private String origin;
	private boolean image;
	private String path;
	private Long bno;
	
	public Attach(String uuid, String origin, boolean image, String path) {
		this.uuid = uuid;
		this.origin = origin;
		this.image = image;
		this.path = path;
	}
	
	
}

AttachDao 생성

package com.chanyongyang.jsp.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import com.chanyongyang.jsp.domain.Attach;
import com.chanyongyang.jsp.domain.Criteria;
import com.chanyongyang.jsp.domain.Member;
import com.chanyongyang.jsp.util.DBConn;

public class AttachDao {

		private Connection conn;
		private PreparedStatement pstmt;
		private ResultSet rs;
		
		public void insert(Attach attach) {
			conn = DBConn.getConnection();
			// 처리할 sql구문, 지정타입에 맞춰서 ? 로 바꿀수 있음 
			String sql = "insert into tbl_attach values (?, ?, ?, ?, ?)";
			try {
				// 문장 생성
				pstmt = conn.prepareStatement(sql);
				int idx = 1;
				pstmt.setString(idx++, attach.getUuid());
				pstmt.setString(idx++, attach.getOrigin());
				pstmt.setBoolean(idx++, attach.isImage());
				pstmt.setString(idx++, attach.getPath());
				pstmt.setLong(idx++, attach.getBno());
				
				// 문장 처리
				pstmt.executeUpdate();
				close();
				
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		public Attach selectOne(String uuid) {
			conn = DBConn.getConnection();
			// 반환 예정 객체
			Attach attach = null;
			// 처리할 sql구문
			String sql = "select * from tbl_attach where uuid = ?";
			try {
				// 문장 생성 conn으로부터 문장생성 객체를 만듬
				pstmt = conn.prepareStatement(sql);
				pstmt.setString(1, uuid);
				// 결과집합 반환
				rs = pstmt.executeQuery();
				
				// 결과집합을 자바객체로 만듬
				if(rs.next()) {
					int idx = 1;
					// 객체 생성 후 값 바인딩
					
					attach = new Attach(
							rs.getString(idx++),
							rs.getString(idx++),
							rs.getBoolean(idx++),
							rs.getString(idx++),
							rs.getLong(idx++));
				}
				close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			// 결과 반환
			return attach;
		}
		
		
		public List<Attach> selectList(Long bno) {
			conn = DBConn.getConnection();
			// 반환 예정 객체
			List<Attach> attachs = new ArrayList<Attach>();
			// 처리할 sql구문
			String sql ="select * from tbl_attach where bno = ?"; 
			// 검색
			try {
				// 문장 생성 conn으로부터 문장생성 객체를 만듬
				pstmt = conn.prepareStatement(sql);
				int idx = 1;
				pstmt.setLong(idx++, bno);
				// 결과집합 반환
				rs = pstmt.executeQuery();
				
				// 결과집합을 자바객체로 만듬
				while(rs.next()) {
					idx = 1;
					// 객체 생성 후 값 바인딩
					
					Attach	attach = new Attach(
								rs.getString(idx++),
								rs.getString(idx++),
								rs.getBoolean(idx++),
								rs.getString(idx++),
								rs.getLong(idx++));
					attachs.add(attach);
				}
				close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			// 결과 반환
			return attachs;
		}
		
		public void delete(Long bno) {
			conn = DBConn.getConnection();
			// 처리할 sql구문
			String sql = "delete from tbl_attach where bno = ?";
			try {
				// 문장 생성
				pstmt = conn.prepareStatement(sql);
				pstmt.setLong(1, bno);
				
				// 문장 처리
				pstmt.executeUpdate();
				close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
		// 자원반환
		public void close() {
			if(conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {}
			}
			if(pstmt != null) {
				try {
					pstmt.close();
				} catch (SQLException e) {}
			}
			if(rs != null) {
				try {
					rs.close();
				} catch (SQLException e) {}
			}
		}

		public static void main(String[] args) {
//			new AttachDao().selectList(1).forEach(System.out::println);
//			AttachDao dao = new AttachDao();
//			Attach attach = dao.selectOne(5L);
//			System.out.println(dao.selectListCount(1));
//			attach.setTitle("java에서 수정한 내용");
//			attach.setContent("java에서 수정한 내용");
			
//			dao.update(attach);
//			dao.delete(5L);
//			System.out.println(dao.selectOne(5L));
			
			String str = "12345";
			String[] result = str.split("");
			System.out.println(result.length);
			String result2 = String.join(" or ", result);
			System.out.println(result2);
		}

			

}

 

예) 비로그인상태에서 글쓰기를 눌렀을 때 로그인 화면이 나오고 로그인시 메인페이지로 가는것 수정

if(!isLogin(req)) {
			resp.sendRedirect(req.getContextPath() + "/member/login?href=" + URLEncoder.encode(req.getRequestURI(), "utf-8"));
			return;
		}

 

예) 비로그인상태에서 댓글 작성시 로그인하고 바로 댓글작성창으로 가게하는것

1. login.java 수정

		String msg = "";
		String redirectStr = req.getContextPath();
		String href = req.getParameter("href");
		switch(memberService.login(id, pw)) {
		case 1:
			req.getSession().setAttribute("member", memberService.get(id));
			if(href != null) {
				redirectStr = href;
			}
//			resp.sendRedirect(req.getContextPath()); //
			break;
		case 2: 
			msg = "아이디가 없습니다";
			msg = URLEncoder.encode(msg, "utf-8");
//			resp.sendRedirect(req.getContextPath() + "/member/login?msg="+msg); //
			redirectStr += "/member/login?msg="+msg;
			if(href != null) {
				redirectStr += "&href=" + URLEncoder.encode(href, "utf-8");
			}
			break;
		case 3: 
			msg = "비밀번호가 일치 하지 않습니다";
			msg = URLEncoder.encode(msg, "utf-8"); // 맨 아래로
//			resp.sendRedirect(req.getContextPath() + "/member/login?msg="+msg);
			redirectStr += "/member/login?msg="+msg;
			if(href != null) {
				redirectStr += "&href=" + URLEncoder.encode(href, "utf-8");
			}
		}

2. view.jsp의 js부분 수정

    $("#commentArea").next().click(function() {
      //console.log($("#commentArea").val()); //session 체크가 필요함
      let content = $("#commentArea").val();
      if(!writer) {
    	  alert("로그인 후 작성하세요");
    	  location.href = contextPath + "/member/login?href=" + encodeURIComponent(location.pathname + location.search + '#replyArea');
    	  return;
      }

 

※ 글목록에서 첨부파일여부

1. BoardServiceImpl get, list 수정

	@Override
	public Board get(Long bno) {
		dao.increaseHitCount(bno);
		Board board = dao.selectOne(bno);
		// 첨부파일 보는것
		board.setAttachs(attachDao.selectList(bno));
		return board;
	}

	@Override
	public List<Board> list(Criteria cri) {
		return dao.selectList(cri);

2. view.jsp 구문 추가

<div class="mb-3">
	<label for="writer" class="form-label">writer</label>
	<input type="text" class="form-control" id="writer" placeholder="Enter writer" name="writer" value="${board.writer}" readonly>
</div>
<div class="mb-3">
	<p class="form-label">files</p>
	<c:forEach items="${board.attachs}" var="attach">
		<p>${attach.origin}</p>
	</c:forEach>
</div>

 

※ 파일 다운로드

1. controller 패키지에 fileDownloader 생성

package com.chanyongyang.jsp.controller;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.chanyongyang.jsp.domain.Attach;
import com.chanyongyang.jsp.util.ParamSolver;

@WebServlet("/download")
public class FileDownloader extends HttpServlet {

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		Attach attach = ParamSolver.getParams(req, Attach.class);
		System.out.println(attach);
		
		// File객체를 만들어서 stream을 뽑아내야함
		File file = new File(ParamSolver.UPLOAD_PATH, attach.getPath());
		String origin = attach.getOrigin();
		// 파일명 중 마지막 .의 위치
		int dotIdx = origin.lastIndexOf(".");

		// 확장자를 담을 변수
		String ext = "";

		// 확장자 구하기
		if (dotIdx > -1) {
			ext = origin.substring(dotIdx); 
		}
		file = new File(file, attach.getUuid() + ext);
		System.out.println(file);
		System.out.println(file.exists());
		
		// 응답 제작 
		resp.addHeader("Content-Disposition", "attachment; filename="+new String(origin.getBytes("utf-8"), "iso-8859-1"));
		
		// input
		// output
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
		byte[] bytes = bis.readAllBytes();
		BufferedOutputStream bos = new BufferedOutputStream(resp.getOutputStream());
		bos.write(bytes);
		bis.close();
		bos.close();
		
	}
	

}

 

※ 게시글 삭제

게시글을 삭제하려면 첨부파일, 댓글이 선행삭제가 되어야 한다.

선행삭제가 되지않고 글을 지우려고하면 에러가 나온다.

package com.chanyongyang.jsp.domain;

import java.io.File;

import com.chanyongyang.jsp.util.ParamSolver;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Attach {
	private String uuid;
	private String origin;
	private boolean image;
	private String path;
	private Long bno;
	
	public Attach(String uuid, String origin, boolean image, String path) {
		this.uuid = uuid;
		this.origin = origin;
		this.image = image;
		this.path = path;
	}
	public String getQueryString() {
		return String.format("%s=%s&%s=%s&%s=%s", "uuid", uuid, "origin", origin, "path", path);
	}
	
	public File getFile() {
		return getFile(false);
	}
	
	public File getFile(boolean thumb) {
		File file = null;
		file = new File(ParamSolver.UPLOAD_PATH, path);
		// 파일명 중 마지막 .의 위치
		int dotIdx = origin.lastIndexOf(".");

		// 확장자를 담을 변수
		String ext = "";

		// 확장자 구하기
		if (dotIdx > -1) {
			ext = origin.substring(dotIdx); 
		}
		file = new File(file, uuid + (thumb ? "_t" : "") + ext);
		return file;
	}
	
}
package com.chanyongyang.jsp.service;

import java.util.List;
import java.util.stream.Collectors;

import com.chanyongyang.jsp.dao.AttachDao;
import com.chanyongyang.jsp.dao.BoardDao;
import com.chanyongyang.jsp.dao.ReplyDao;
import com.chanyongyang.jsp.domain.Attach;
import com.chanyongyang.jsp.domain.Board;
import com.chanyongyang.jsp.domain.Criteria;

public class BoardServiceImpl implements BoardService {
	private BoardDao dao = new BoardDao();
	private AttachDao attachDao = new AttachDao();
	private ReplyDao replyDao = new ReplyDao();
	
	
	// 서비스의 존재의 의의 : 트랜젝션의 기준
	@Override
	public Long register(Board board) {
		// 글 작성 후 글번호 지정
		Long bno = (long)dao.insert(board);
		System.out.println("boardService.register() :: " + bno);
		// list에 하나씩 바인드
		for(Attach attach : board.getAttachs()) {
			attach.setBno(bno);
			attachDao .insert(attach);
		}
		
		return bno;
	}

	@Override
	public Board get(Long bno) {
		dao.increaseHitCount(bno);
		Board board = dao.selectOne(bno);
		// 첨부파일 보는것
		board.setAttachs(attachDao.selectList(bno));
		return board;
	}

	@Override
	public List<Board> list(Criteria cri) {
		return dao.selectList(cri);
//		return dao.selectList(cri).stream().map(board -> {
//			board.setAttachs(attachDao.selectList(board.getBno()));
//			return board;
//		}).collect(Collectors.toList());
	}

	@Override
	public void modify(Board board) {
		dao.update(board);
	}

	@Override
	public void remove(Long bno) {
		// 파일시스템에 존재하는 파일 삭제
		attachDao.selectList(bno).forEach(attach -> { 
			attach.getFile().delete();
			if(attach.isImage()) {
				attach.getFile(true).delete();
			}
		});
		// 첨부 목록 삭제
		attachDao.delete(bno);
		
		// 댓글 삭제
//		replyDao.delete(bno);
		
		// tbl_board 삭제
		dao.delete(bno);
	}

	@Override
	public int listCount(Criteria cri) {
		return dao.selectListCount(cri);
	}
	
}

'Servlet, JSP > Servlet, JSP' 카테고리의 다른 글

23-03-13 Servlet, JSP  (0) 2023.03.13
23-03-09 (1) Servlet, JSP  (0) 2023.03.09
23-03-07(1) Servlet, JSP  (0) 2023.03.07
23-03-06 (1) servlet, jsp  (0) 2023.03.06