파일 입출력
몇 가지 사전 작업이 필요하다.
우선 사진이 저장될 테이블 생성이 필요!
그럼 이제 이 데이터를 볼 수 있는 상세페이지가 필요하므로
board.jsp 파일을 새로 만들어준다!
// 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>
<html>
<head>
<meta charset="UTF-8">
<title>글 상세페이지</title>
<script src="js/preview.js"></script>
</head>
<body>
내용 ${data.content} <br>
작성자 ${data.writer} <br>
<hr>
<c:if test="${empty path}">
<img alt="기본 이미지" src="images/default.png">
</c:if>
<c:if test="${not empty path}">
<img alt="비워두면 안됨" src="images/${path}">
</c:if>
<form action="updateBoard.do" method="POST" enctype="multipart/form-data">
<input type="hidden" name="bid" value="${data.bid}">
이미지 <input type="file" name="file" onchange="preview(event)"> <br>
<img id="previewImage" style="display:none;margin:5px;" alt="미리보기 이미지"><br>
<input type="submit" value="이미지 변경">
</form>
<hr>
<a href="main.do">메인으로 이동</a>
</body>
</html>
<c:if> 태그를 사용해서 이미지가 있는 상태일 때와 없는 상태일 때를 구분하여 보여준다.
c 태그를 사용했기 때문에 상단에 태그 사용할 수 있도록 선언 필요하고,
이 상세페이지에서 사진 수정이 가능하기 때문에 데이터를 넘겨줘야 하므로 form 태그 사용해서 작성한다.
이때 이 form은 이미지만을 다루기 때문에 enctype="multipart/form-data" 로 설정하고,
bid의 경우 view에서 넘겨주는 데이터라서, 사용자에게 보이지 않도록 hidden으로 설정해준다.
이미지가 수정되는 경우 보통 미리 보기가 있는데
이 미리 보기는 스크립트를 통해 가능 --> js 파일 별도로 만들어서 해당 jsp 파일에 연결해 준다.
하나 체크해야 할 사항은?
이 jsp 파일에서 필요한 데이터는 data와 path이다. 여기서는 updateBoard.do를 할 수 있다!
// preview.js
function preview(event) {
const file = event.target.files[0]; // 사용자가 선택한 파일 목록에서 첫 번째 파일을 가져와 file 변수에 저장
const reader = new FileReader(); // 파일을 읽을 수 있도록 해주는 메서드
reader.onload = function() { // 파일읽기가 완료됐을 때 실행
// id 요소를 찾아서 변수에 저장한다.
const imgPreview = document.getElementById('previewImage');
// reader.result ==> 읽어온 파일의 데이터 URL
// 이 URL을 미리보기 이미지의 src 속성에 할당
imgPreview.src = reader.result;
// 미리보기 이미지 스타일을 변경
imgPreview.style.display = 'block';
};
if (file) { // 사용자가 파일을 선택했다면
// 파일을 데이터 url로 읽기 시작
reader.readAsDataURL(file);
}
}
사진을 미리 보기 할 수 있는 js 코드이다.
게시판 목록에서 글을 선택해 상세페이지로 갈 수 있도록 main도 경로를 달아주었다.
// main.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>
<html>
<head>
<meta charset="UTF-8">
<title>메인 페이지</title>
<link rel="stylesheet" href="css/style.css" type="text/css">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="js/boardList.js"></script>
</head>
<body>
<h1>${userID}님, 안녕하세요! :D</h1>
<div class="header">
${boardDTO} | ${boardDTO.bid} | ${boardDTO.content}
<a href="logout.do" class="logout-link">로그아웃</a>
</div>
<hr>
<div>
<h3>글 목록</h3>
<div class="action-links">
<a href="insertBoard.do">글 작성</a>
<a href="main.do" id="showBoardList">글 전체 목록 보기</a>
</div>
</div>
<form method="GET" id="searchBoardForm">
<select name="condition" id="condition">
<option value="CONTENT" ${boardDTO.condition eq 'CONTENT' ? 'selected' : ''}>내용</option>
<option value="WRITER" ${boardDTO.condition eq 'WRITER' ? 'selected' : ''}>작성자</option>
</select>
<input type="text" name="keyword" id="keyword" placeholder="검색어를 입력해 주세요." required>
<input type="submit" value="검색">
</form>
<ul id="postList">
<c:if test="${empty datas}">
<li>검색결과가 없습니다.</li>
</c:if>
<c:if test="${not empty datas}">
<c:forEach var="data" items="${datas}">
<li>
<a href="board.do?bid=${data.bid}">
<div class="content">${data.content}</div>
<div class="writer">작성자 : ${data.writer}</div>
</a>
</li>
</c:forEach>
</c:if>
</ul>
<hr>
</body>
</html>
content와 writer이 나오는 div 박스를 a 태그로 감싸주어서 글을 선택 시
상세 페이지로 이동할 수 있도록 작성해 주었다.
(마우스 가져다 대면 파란색 줄이 뜨면서 반응함)
위에서 얘기했듯이 board.jsp에서는 data와 path가 필요하다.
Controller에서 이미지 파일을 손쉽게 보낼 수 있도록 MultipartFile을 사용한다.
MultipartFile이란?
Spring에서 제공하는 파일 업로드 처리에 사용되는 인터페이스이다.
파일 업로드를 간편하게 처리할 수 있도록 도와준다.
image 테이블을 생성했기 때문에 DTO와 DAO 생성이 필요하다.
이때 DTO의 멤버변수로 MultipartFile 속성의 멤버변수를 등록해 준다.
MultipartFile import는 아래와 같이 나오는데,
import org.springframework.web.multipart.MultipartFile;
public class ImageDTO {
private int imageid; // 사진 id (PK)
private String path; // 사진 경로
private int bid; // 글 번호 (FK)
private MultipartFile file; // Spring에서 파일 업로드 처리하기 위한 인터페이스
간혹 자동으로 제공을 하지 않는 경우가 있다!
그런 경우는 의존주입을 해주면 된다.
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
이 MultipartFile를 사용하기 위해서는 Resolver가 필요하다.
<!-- multipartFile 사용하기 위해 추가 -->
<bean class="org.springframework.web.multipart.support.StandardServletMultipartResolver" id="multipartResolver" />
이것도 Resolver류이기 때문에 이름을 정확하게 작성해야 한다.
main.jsp에서 게시글 상세페이지로 보내는 board.do 요청과
상세페이지에서 이미지 업데이트를 하는 updateBoard.do 요청에 대한 Controller 코드가 필요하다.
// Controller_board.do
// 글 상세페이지 출력
@RequestMapping(value="/board.do")
public String selectBoard(BoardDTO boardDTO, ImageDTO imageDTO, Model model) {
// 글 번호를 받아와서 상세페이지로 데이터 전달 및 이동시켜줌
// data와 path가 필요하다.
// 글 번호를 service로 보내서 data / path 반환받아 view 전달 필요
System.out.println("com.koreait.app.view.board BoardController selectBoard 시작");
System.out.println("com.koreait.app.view.board BoardController selectBoard boardDTO ["+boardDTO.getBid()+"]");
// 게시글 찾기
boardDTO = boardService.selectOne(boardDTO);
// 사진 찾기
imageDTO = imageService.selectOne(imageDTO);
// 데이터 로그
System.out.println("com.koreait.app.view.board BoardController selectBoard boardDTO ["+boardDTO+"]");
System.out.println("com.koreait.app.view.board BoardController selectBoard imageDTO ["+imageDTO+"]");
// 반환받은 객체를 model 객체에 저장한다. (v에게 전달하기 위함)
model.addAttribute("data", boardDTO);
if(imageDTO != null) { // 만약 반환받은 imageDTO에 데이터가 있다면
// 반환받은 객체를 model 객체에 저장한다. (v에게 전달하기 위함)
model.addAttribute("path", imageDTO.getPath());
}
// 데이터를 찾아서 board.jsp로 보내줘야 한다.
// 데이터를 보내야 하므로 포워드
System.out.println("com.koreait.app.view.board BoardController selectBoard 종료");
return "board";
}
글 상세페이지가 추가되면서 위에 언급했던 data와 path가 필요하다.
따라서 이곳에서 커맨드 객체로 boardDTO와 imageDTO 둘 다 반영해 주고,
글 번호를 보내 selectOne으로 데이터를 받아온다.
// Controller_updateBoard.do
@RequestMapping(value="/updateBoard.do")
public String imageUpload(ImageDTO imageDTO) throws IllegalStateException, IOException {
System.out.println("com.koreait.app.view.board BoardController imageUpload 시작");
System.out.println("com.koreait.app.view.board BoardController imageUpload imageDTO ["+imageDTO+"]");
MultipartFile file = imageDTO.getFile(); // DTO에서 MultipartFile 객체를 가지고 옴 >> 업로드 된 파일 정보를 가지고 있음
String fileName = file.getOriginalFilename(); // 원본 파일명
System.out.println("com.koreait.app.view.board BoardController imageUpload 파일명 ["+fileName+"]");
file.transferTo(new File(PATH+fileName)); // 업로드 된 파일을 서버의 경로에 저장한다.
imageDTO.setPath(fileName);
System.out.println("Inserting image with BID: " + imageDTO.getBid() + " and path: " + imageDTO.getPath());
boolean flag = imageService.update(imageDTO);
if(flag) {
System.out.println("com.koreait.app.view.board BoardController imageUpload 파일 insert 성공");
}
else {
System.out.println("com.koreait.app.view.board BoardController imageUpload 파일 insert 실패");
}
System.out.println("com.koreait.app.view.board BoardController imageUpload 파일 insert 종료");
return "redirect:main.do";
}
이미지 업로드 시 MultipartFile 객체를 사용하여 업로드 한 파일의 정보를 가지고 오고,
이미지 복사의 개념이기 때문에 원본 파일명을 가져와 변수에 저장해 준다.
그 후 transferTo 메서드를 사용하여 업로드된 파일을 서버의 경로에 저장시킨다.
저장할 때 인자로 File 객체가 들어가는데, 경로와 + 파일명을 적어준다. ==> 이것이 파일의 경로가 된다.
파일선택을 눌러 사진을 선택하면
미리 보기가 아래에 나오게 된다.
여기서 이미지 변경을 누르면 main페이지로 가는데,
다시 해당 글을 선택하면
업로드한 이미지로 변경이 된다.
🍀 정리
main에서 board.do를 통해 게시글 상세 보기가 가능해졌고,
상세 보기에서는 updateBoard.do를 통해 데이터를 바꾸거나 추가할 수 있다.
그럼 Controller에서는 imageDTO를 받아올 수 있는데
이 과정에서 MultipartFile 이 필요하고, 이를 관리하는 Resolver가 필요하게 된다.
우리는 이 Resolver를 서블릿에 등록해줘야 한다.
Controller에서는 getFile을 통해 파일명을 알아내거나 transferTo를 통해 파일을 생성시킬 수 있다.
1)
여기까지 했을 때 버전 때문에 에러 나는 경우가 있다.
만약 잘 안된다면 Servers의 context.xml에 가서
<Context allowCasualMultipartParsing="true"> 를 추가해 준다.
2)
사진 업로드 하게 되면 변경된 이미지를 보여주기 전까지 로딩이 꽤나 걸린다....
window - Preferences - Workspace에서 즉시 새로고침 설정을 해줄 수 있다.
'Spring' 카테고리의 다른 글
[Mybatis] 마이바티스의 특징과 Eclipse 플러그인 설치 (2) | 2024.11.04 |
---|---|
[Spring] 횡단 로직 정리 (0) | 2024.10.28 |
[Spring] 트랜잭션 (0) | 2024.10.21 |
[Spring] 템플릿 패턴 (0) | 2024.10.20 |
[Spring] AOP 어노테이션 사용 (0) | 2024.10.16 |