221102 Spring boot 이미지 업로드

wonny·2022년 11월 2일
0

수업 복습

목록 보기
10/14

SpringBootJpaBoardEx4

bood.day1102.board>SpringJpaBoardEx4Application.java

package boot.day1102.board;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication
@ComponentScan({"boot.board.*"})
@EntityScan("boot.board.data") //dto 인식
@EnableJpaRepositories("boot.board.data") //dao 인식
public class SpringBootJpaBoardEx4Application {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootJpaBoardEx4Application.class, args);
	}

}

boot.board.data>BoardDto.java

@Entity
@Table(name = "jpaboard")
@Data
public class BoardDto {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private Long num; //long이 아니고 int인 경우 Interface에서 integer로 줘야 함
	
	@Column(name = "writer", updatable = false)
	private String writer;
	
	@Column(name = "subject")
	private String subject;
	
	@Column(name = "content")
	private String content;
	
	@Column(name = "photo")
	private String photo;
	
	@CreationTimestamp
	@Column(name = "writeday", updatable = false)
	private Timestamp writeday;
	
}

boot.board.data>BoardDaoInter.java

package boot.board.data;

import org.springframework.data.jpa.repository.JpaRepository;

public interface BoardDaoInter extends JpaRepository<BoardDto, Long>{

}

application.yml

원래는 application.properties인데 rename에서 .yml로 바꿔주었다.
yml은 properties와 달리 처음 세팅할 때 반복적으로 작성해야 하는 부분을 생략할 수 있어 편리하다.

server:
  port: 9001
  
spring:
  mvc:
    view:
      prefix: /WEB-INF/
      suffix: .jsp
  devtools:
    livereload:
      enabled: true

  #mysql
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver 
    url: jdbc:mysql://localhost:3306/sist?serverTimezone=Asia/Seoul
    username: dragon
    password: 1234
  
  #jpa
  jpa:
    hibernate:
      ddl-auto: update
    generate-ddl: true
    show-sql: true
    database: mysql
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect

여기까지 하면 db에 jpaboard 테이블이 생성된 것을 확인할 수 있다. 그리고 hibernate_sequence는 어제 만들었던 데이터까지 포함되어 업데이트 되어 있는 것을 볼 수 있다.

BoardDao.java

package boot.board.data;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Repository;

@Repository
public class BoardDao {
	
	@Autowired
	BoardDaoInter daoInter;
	

}

BoardController.java

@Controller
public class BoardController {

	@Autowired
	BoardDao dao;
	
	//처음 열었을 때 board/list로 나오게
	@GetMapping("/")
	public String home() {
		return "redirect:board/list";
	}
	
	@GetMapping("/board/list")
	public ModelAndView list() {
		
		ModelAndView mview = new ModelAndView();
	
		
		mview.setViewName("board/list");
		
		return mview;
	}
	
	
	//writeform으로 가게
	@GetMapping("/board/addform")
	public String form() {
		return "board/writeform";
	}

추가

BoardDao.java

//insert
	public void insertBoard(BoardDto dto) {
		daoInter.save(dto);
	}

writeform.jsp

<body>
<div style="margin: 50px 50px;">
	<form action="insert" method="post" enctype="multipart/form-data">
		<table class="table table-bordered" style="width: 500px;">
			<caption>새 글 작성</caption>
				<tr>
					<th width="80" bgcolor="pink">작성자</th>
					<td>
						<input type="text" name="writer" required="required" class="form-control" style="width: 150px;">
					</td>
				</tr>
				<tr>
					<th width="80" bgcolor="pink">제목</th>
					<td>
						<input type="text" name="subject" required="required" class="form-control" style="width: 400px;">
					</td>
				</tr>
				<tr>
					<th width="80" bgcolor="pink">사진</th>
					<td>
						<input type="file" name="upload" class="form-control" style="width: 300px;"> <!-- 사진의 name은 db와 다르게 하는 것이 좋다 -->
					</td>
				</tr>
				<tr>
					<td colspan="2" align="center">
						<textarea style="width: 480px; height: 150px;" name="content" required="required"></textarea>
					</td>
				</tr>
				<tr>
					<td colspan="2" align="center">
						<button type="submit" class="btn btn-success">저장</button>
						<button type="button" class="btn btn-info" onclick="location.href='list'">목록</button>
					</td>
				</tr>
		</table>
	</form>	

</div>
</body>

form action="insert" method="post" enctype="multipart/form-data": 파일 업로드할 때는 enctype="multipart/form-data"를 꼭 손으로 써줘야 한다.

BoardController.java

//insert
	@PostMapping("/board/insert")
	public String insert(@ModelAttribute BoardDto dto,
			@RequestParam MultipartFile upload, //파일 여러개 업로드의 경우 arraylist Multi~ 
			HttpSession session) { 
		
		//업로드 될 실제 경로
		String realPath=session.getServletContext().getRealPath("/save");
		
		String uploadname=upload.getOriginalFilename();
		
		if(upload.isEmpty())
			dto.setPhoto("no");
		else {
			dto.setPhoto(uploadname);
		}

		//실제 업로드
		try {
			upload.transferTo(new File(realPath+"\\"+uploadname));
		} catch (IllegalStateException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		//db에 저장
		dao.insertBoard(dto);
		
		return "redirect:list";
		
	}

업로드 한 파일은 C:\sist0711\bootwork\SpringBootJpaBoardEx4\src\main\webapp\save 폴더에서 확인할 수 있다.

전체 출력

BoardDao.java

//list
	public List<BoardDto> getAllList(){
		return daoInter.findAll(Sort.by(Sort.Direction.DESC,"num"));
	}

findAll(Sort.by(Sort.Direction.DESC,"num"))는 num 기준 내림차순으로 조회한다는 뜻

BoardController.java

@GetMapping("/board/list")
	public ModelAndView list(@ModelAttribute BoardDto dto) {
		
		ModelAndView mview = new ModelAndView();
		
		//리스트 dao로부터 가져오기
		List<BoardDto> list = dao.getAllList();
		
		//request에 저장
		mview.addObject("list", list);
		mview.addObject("totalCount", list.size());
		
		mview.setViewName("board/list");
		
		return mview;
	}

list.jsp

<body>
<div style="margin: 50px 50px;">
	<button type="button" class="btn btn-info" onclick="location.href='addform'">글쓰기</button>
	<br>
	<h3>총 ${totalCount }개의 게시물이 있습니다.</h3>
	<br>
	<table class="table table-bordered" style="width: 800px; text-align: center" >
		<tr bgcolor="pink">
			<th width="60" style="text-align: center">번호</th>
			<th width="500" style="text-align: center">제목</th>
			<th width="120" style="text-align: center">작성자</th>
			<th width="150" style="text-align: center">작성시간</th>
		</tr>
		
		<c:if test="${totalCount==0 }">
			<tr>
				<td colspan="4" align="center">
					<b>등록된 게시물이 없습니다.</b>					
				</td>
			</tr>
		</c:if>
		
		<c:if test="${totalCount>0 }">
			<c:forEach var="dto" items="${list}" varStatus="i">
				<c:set var="n" value="${totalCount-i.index}"/>
					<tr>
						<td>${n}</td>
						<td>
							<a href='detail?num=${dto.num}'>${dto.subject}</a>
							<c:if test="${dto.photo!='no'}">
								&nbsp;&nbsp;<span class="glyphicon glyphicon-picture" style="color: gray; font-size: 0.9em;"></span>
							</c:if>
						</td>
						<td>${dto.writer }</td>
						<td><fmt:formatDate value="${dto.writeday}" pattern="yyyy-MM-dd HH:mm"/></td>
					</tr>
			</c:forEach>
		</c:if>
	
	</table>

</div>
</body>

상세보기

BoardDao.java

//num에 맞는 데이터 
	public BoardDto getData(Long num) {
		return daoInter.getById(num);
	}

BoardController.java

//제목 눌렀을 때 detail.jsp+기존 데이터 뜨게 하기
	@GetMapping("/board/detail")
	public String detail(@RequestParam Long num, Model model) {
		
		BoardDto dto = dao.getData(num);
		
		//내용 줄바꿈 적용하기
		String content = dto.getContent().replace("\n", "<br>");
		dto.setContent(content);
		
		model.addAttribute("dto", dto);
		
		return "board/detail";
	}
//ModelAndView 방식
	@GetMapping("/board/detail") 
	public ModelAndView detail(@RequestParam Long num) {
	
	ModelAndView mview = new ModelAndView();
	
	//dao로부터 dto 얻기 
	BoardDto dto = dao.getData(num);
	 
	mview.addObject("dto", dto);
	 
	mview.setViewName("board/detail");
	 
	return mview; 
    
    }

detail.jsp

</head>
<body>
<div style="margin: 50px 50px;">
	<table class="table table-bordered" style="width: 600px;">
		<caption><h3>게시물 상세보기</h3></caption>
		<tr>
			<th style="background-color: pink; width: 80px;">제목</th>
			<td colspan="3">
				${dto.subject}
			</td>
		</tr>	
		<tr>
			<th style="background-color: pink; width: 80px;">작성자</th>
			<td width="250">
				${dto.writer}
			</td>
			<th style="background-color: pink; width: 80px;">작성일</th>
			<td>
				<fmt:formatDate value="${dto.writeday}" pattern="yyyy-MM-dd HH:mm"/>
			</td>
		</tr>
		<tr>
			<td colspan="4" style="height: 200px;">
				<c:if test="${dto.photo!='no' }">
					<img alt="" src="../save/${dto.photo }" width="200" height="200">
					<br><br>
				</c:if>
				${dto.content }
			</td>
		</tr>
		<tr>
			<td colspan="4" align="center">
				<div style="float: left">
				<button type="button" class="btn btn-primary" onclick="location.href='addform'">글쓰기</button>
				<button type="button" class="btn btn-success" onclick="location.href='list'">목록</button>
				</div>
				<div style="float: right;">
				<button type="button" class="btn btn-warning" onclick="location.href='updateform?num=${dto.num}'">수정</button>
				<button type="button" class="btn btn-danger" onclick="remove(${dto.num})">삭제</button>
				</div>
			</td>
		</tr>
		
		
	</table>

</div>

수정

BoardController.java

@GetMapping("/board/updateform")
	public String uform(@RequestParam Long num, Model model) {

		BoardDto dto = dao.getData(num);
		
		model.addAttribute("dto", dto);
		
		return "board/updateform";
	}
	

updateform.jsp

<script type="text/javascript">
	function readURL(input) {
	    if (input.files && input.files[0]) {
	        var reader = new FileReader();
	        reader.onload = function (e) {
	            $('#blah').attr('src', e.target.result);
	        }
	        reader.readAsDataURL(input.files[0]);
	    }
	}
</script>
</head>
<body>
<div style="margin: 50px 50px;">
	<form action="update" method="post" enctype="multipart/form-data">
		<input type="hidden" name="num" value="${dto.num }">
		<table class="table table-bordered" style="width: 500px;">
			<caption>글 수정하기</caption>
				<tr>
					<th width="80" bgcolor="pink">작성자</th>
					<td>
						<input type="text" name="writer" required="required" class="form-control" style="width: 150px;" value="${dto.writer }" readonly="readonly">
					</td>
				</tr>
				<tr>
					<th width="80" bgcolor="pink">제목</th>
					<td>
						<input type="text" name="subject" required="required" class="form-control" style="width: 400px;" value="${dto.subject }">
					</td>
				</tr>
				<tr>
					<th width="80" bgcolor="pink">사진</th>
					<td>
						<input type="file" name="upload" class="form-control" style="width: 300px;" onchange="readURL(this);"> <!-- 사진의 name은 db와 다르게 하는 것이 좋다 -->
						<div>
						    <img id="blah" src="../save/${dto.photo }" alt="이미지 없음"  style="width: 150px; height: 150px;" />
						</div>
					</td>
				</tr>
				<tr>
					<td colspan="2" align="center">
						<textarea style="width: 480px; height: 150px;" name="content" required="required">${dto.content }</textarea>
					</td>
				</tr>
				<tr>
					<td colspan="2" align="center">
						<button type="submit" class="btn btn-success">수정</button>
						<button type="button" class="btn btn-info" onclick="location.href='list'">목록</button>
					</td>
				</tr>
		</table>
	</form>	

</div>


</body>
<div>
						    <img id="blah" src="../save/${dto.photo }" alt="이미지 없음"  style="width: 150px; height: 150px;" />
						</div>

이미지 태그의 src에 "../save/${dto.photo}"를 넣어주면 수정폼을 열었을 때 db에 저장되어 있는 사진이 이미지 미리보기 공간에 뜬다. 그리고 파일 업로드에서 다른 파일을 선택한 경우 선택한 파일이 뜨도록 스크립트를 만들어주었다.

BoardController.java

//update
	@PostMapping("/board/update")
	public String update(@ModelAttribute BoardDto dto,
			@RequestParam MultipartFile upload, //파일 여러개 업로드의 경우 arraylist Multi~ 
			HttpServletRequest request) { 
		
		//업로드 될 실제 경로
		String path=request.getServletContext().getRealPath("/save");
		
		//기존 사진 파일명
		String oldfilename = dao.getData(dto.getNum()).getPhoto();
		
		
		if(upload.isEmpty()) //사진을 안바꾸면 photo에 기존 파일 이름
			dto.setPhoto(oldfilename);
		else {
			//새로 업로드 한 파일명
			String newfilename=upload.getOriginalFilename();
			
			//dto 저장
			dto.setPhoto(newfilename);
			
			//수정 전 사진 삭제
			deleteFile(path, oldfilename);
			
			//실제 업로드
			try {
				upload.transferTo(new File(path+"\\"+newfilename));
			} catch (IllegalStateException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		
		//db에 수정해서 저장
		dao.updateBoard(dto);
		
		return "redirect:detail?num="+dto.getNum();
		
	}
    
    //이미지 파일 삭제 메서드
	public void deleteFile(String path, String fileName) {
		File file = new File(path+"\\"+fileName);
		
		if(file.exists()) { //해당 경로에 파일이 있는 경우 true 값 반환
			file.delete();
			System.out.println("파일 삭제 완료");
		}
			
	}

게시물을 수정할 때 파일을 새로 업로드 하지 않는 경우 기존의 이미지가 db에 그대로 있어야 하고, 새로 업로드 하는 경우 새로운 이미지가 db에 반영되어야 한다. updateform의 upload가 empty인 경우는 수정폼에서 새로 이미지 업로드를 하지 않은 경우이기 때문에 dto.setPhoto(oldfilename)을 해준다. upload가 empty가 아닌 경우는 새로 이미지를 업로드했다는 뜻이기 때문에 dto.setPhoto(newfilename)을 해주고, 원래 db에 있던 이미지가 삭제될 수 있도록 삭제 메서드를 적용해준다.

삭제

detail.jsp

<script type="text/javascript">
	function remove(num){
		//alert(num);
		var ans = confirm("삭제하시겠습니까?");
		if(ans){
			location.href="delete?num="+num;
		}
	}
</script>

삭제 버튼을 눌렀을 때 confirm창이 뜨고 '확인'을 누르면 delete 매핑 주소로 이동하여 삭제되도록 한다.

BoardDao.java

//delete
	public void deleteBoard(Long num) {
		daoInter.deleteById(num);
	}

BoardController.java

//이미지 파일 삭제 메서드
	public void deleteFile(String path, String fileName) {
		File file = new File(path+"\\"+fileName);
		
		if(file.exists()) { //해당 경로에 파일이 있는 경우 true 값 반환
			file.delete();
			System.out.println("파일 삭제 완료");
		}
			
	}
	
	//삭제 버튼 눌렀을 때 파일+db 삭제
	@GetMapping("board/delete")
	public String delete(@RequestParam Long num,
			HttpServletRequest request) {
		
		String path = request.getServletContext().getRealPath("/save");
		String filename = dao.getData(num).getPhoto();
		
		//사진 삭제
		deleteFile(path, filename);
		//db에서도 삭제
		dao.deleteBoard(num);
		
		return "redirect:list";
	}

삭제메서드를 통해 업로드 된 사진 파일까지 지워질 수 있도록 한다.

profile
하다 보면 되겠지요..!

0개의 댓글