@AllArgsConstructor
@Controller
public class ImageController {
private final ImageService imageService;
@GetMapping({"/", "image/story"})
public String story() {
return "image/story";
}
@GetMapping("/image/popular")
public String popular() {
return "image/popular";
}
@GetMapping("/image/upload")
public String upload() {
return "image/upload";
}
@PostMapping("/image")
public String imageUpload(ImageUploadDto imageUploadDto , @AuthenticationPrincipal PrincipalDetails principalDetails){
//서비스 호출
imageService.사진업로드(imageUploadDto,principalDetails);
return "redirect:/user/" +principalDetails.getUser().getId();
}
}
이미지 컨트롤러에 PostMapping 으로 업로드 관련 컨트롤러를 추가한다.
return 은 return 하고 /user/(유저id값) 으로 돌아갈 수 있도록 구현
그 다음은 Image Entity를 만든다.
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
@Entity
public class Image {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String caption;
private String postImageUrl; // 사진을 전송받아서 그 사진을 서버에
// 폴더에 저장하게될것 - DB에는 경로를 인서트
@JoinColumn(name="userId") //foreign 키 이름 지정
@ManyToOne // 한명이 많은 이미지를 올릴 수 있고 이미지는 하나의 이미지가 여럿일수없으니
private User user; // db에 오브젝트 자체를 저장할 순 없고 이대로면 foreign key로 저장됨
//이미지 좋아요
//이미지 댓글
private LocalDateTime createDate;
@PrePersist
public void createDate() {
this.createDate = LocalDateTime.now();
}
}
foreign key 이름 지정 해주고 createDate도 넣는다 postImageUrl은 추후 서버 폴더에 저장될 경로가 될것
그 후 ImageRepository 생성해서 jpa로 연결
//코드블럭은 생략
그리고 실질적으로 사진을 첨부할 수 있게 할수있는 DTO를 생성
package com.pyo.pyostagram.web.dto.image;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
@Data
public class ImageUploadDto {
private MultipartFile file;
private String caption;
}
file 은 꼭 MultipartFile 형식으로 지정
이제 ImageService를 구현
@RequiredArgsConstructor
@Service
public class ImageService {
private final ImageRepository imageRepository;
@Value("${file.path}") // yml에 적힌값 가져오는거
private String uploadFolder;
public void 사진업로드(ImageUploadDto imageUploadDto, PrincipalDetails principalDetails){
UUID uuid = UUID.randomUUID(); //uuid 하는 이유 = 만약 같은이름의 파일이 uuid 없이 올라온다면 덮어씌워지기 때문에
String imageFileName= uuid+"_"+ imageUploadDto.getFile().getOriginalFilename(); //1.jpg
System.out.println("이미지 파일 이름 =" + imageFileName);
Path imageFilePath = Paths.get(uploadFolder+imageFileName); //실제 경로 적어야함
//통신 , I/O 일어날때는 예외가 발생할 수 있기때문에 예외처리 해야함
try{
Files.write(imageFilePath,imageUploadDto.getFile().getBytes());
//첫번째로 경로,2. 실제 파일을 byte화 해서 넣어야함, 3.옵션값인데 생략가능
}catch(Exception e){
e.printStackTrace();
}
}
}
@Value 를 통해서 yml에 적힌 경로값을 가져오고
업로드시 UUID 처리를 꼭 해줘야 한다 그 이유는 같은 이름의 파일이 uuid 없이 올라오게 된다면 폴더에서 덮어씌워질것이기 때문
이제 JSP 가서
<!--사진업로드 Form-->
<form class="upload-form" action="/image" method="post" enctype="multipart/form-data">
<!-- enctype이 multipart~ 인 이유는 input type="file"은 byte화 해서 전송하고 text는 key/value 여서 서로 방식이라 그렇다. -->
여기서 중요한것은 enctype 이다. enctype을 적는 이유는 Post되는 형식이 input type="file" 의 경우는 byte화 되서 들어가고
input type="text"의 경우 key/value 값으로 들어가기 때문에 두가지 동시에 보낼수 있는 multipart/form-data 형식을 사용하는것
이제 DB에 이미지를 적용해야하는데,
@Data
public class ImageUploadDto {
private MultipartFile file; //멀티파츠는 @NotBlank 지원하지않음
private String caption;
public Image toEntity(String postImageUrl, User user){
return Image.builder()
.caption(caption)
.postImageUrl(postImageUrl)
.user(user)
.build();
}
}
ImageUploadDto 에 toEntitiy 메서드를 만든후 Dto에서 가공해서 domain으로 가져갈 데이터들을 빌드해준다.
여기서 user정보랑 DB에 들어갈 url --> 우리가 DB에서 postImageUrl , 즉 UUID가 포함된 image의 url을 넣을것이기 때문에 service에서 받아오면 된다.
@Value("${file.path}") // yml에 적힌값 가져오는거
private String uploadFolder;
public void 사진업로드(ImageUploadDto imageUploadDto, PrincipalDetails principalDetails){
UUID uuid = UUID.randomUUID(); //uuid 하는 이유 = 만약 같은이름의 파일이 uuid 없이 올라온다면 덮어씌워지기 때문에
String imageFileName= uuid+"_"+ imageUploadDto.getFile().getOriginalFilename(); //1.jpg
System.out.println("이미지 파일 이름 =" + imageFileName);
Path imageFilePath = Paths.get(uploadFolder+imageFileName); //실제 경로 적어야함
//통신 , I/O 일어날때는 예외가 발생할 수 있기때문에 예외처리 해야함
try{
Files.write(imageFilePath,imageUploadDto.getFile().getBytes());
//첫번째로 경로,2. 실제 파일을 byte화 해서 넣어야함, 3.옵션값인데 생략가능
}catch(Exception e){
e.printStackTrace();
}
Image image = imageUploadDto.toEntity(imageFileName,principalDetails.getUser());
Image imageEntity = imageRepository.save(image);
}
}
그 후 Image 타입의 image를 선언하고 Dto의 toEntity 메서드에 파일이름과 유저를 담아서 보낸 후 인서트 하면 된다.
그리고
@PostMapping("/image")
public String imageUpload(ImageUploadDto imageUploadDto , @AuthenticationPrincipal PrincipalDetails principalDetails){
//서비스 호출
if(imageUploadDto.getFile().isEmpty()){
throw new CustomValidationException("이미지가 첨부되지 않았습니다.",null);
}
imageService.사진업로드(imageUploadDto,principalDetails);
return "redirect:/user/" +principalDetails.getUser().getId();
}
이 과정에서 우리는 ImageService에 이미지가 첨부되지 않은 경우를 위해 예외처리를 해줘야한다.
이유는 무조건 사진 업로드를 하기위한 환경을 만들기 위함이고
저번처럼 Dto 에서 @NotBlank 해줘도 되지않나요 하실수 있지만 MultiPartFile 형식은 @NotBlank가 안됀다.
그래서 아래처럼 로직을 구현할것이다.
그리고 throw new CustomValidationException 값의 errorMap이 null인 이유는 우리가 @Valid를 사용해서 BindingResult를 사용한게 아니기 때문에 null로 보낸다.
@ExceptionHandler(CustomValidationException.class)
public String validationException(CustomValidationException e) {
//CMRespDto, Script 비교
// 1. 클라이언트한테 대답하면 Script가 좋음
// 2. Ajax통신 - CMRespDto가 좋음
// 3. android 통신 - CMRRespDto
if (e.getErrorMap() == null) {
return Script.back(e.getMessage());
} else {
return Script.back(e.getErrorMap().toString());
}
}
파일 첨부에선 getErrorMap이 null 이기 때문에 validationException 에 ErrorMap이 null일 경우를 대비하여 if문을 넣어주자.
if 분기에따라 errorMap이 null 이면서 이미지가 첨부되지 않았을 때의 alert 창. 잘뜬다.
'Spring > JPA + Security' 카테고리의 다른 글
[Spring Data JPA] 게시판 띄우는 로직 만들기 1-1 무한 참조 오류 (0) | 2023.01.10 |
---|---|
[Spring Data JPA] 게시판 띄우는 로직 만들기 - 1 양방향 맵핑 및 이미지 뷰 렌더링 (0) | 2023.01.10 |
[Spring Data JPA] JPA를 사용한 구독하기 3 - 구독하기 로직 구현하기 (0) | 2023.01.08 |
[Spring Data JPA] JPA를 사용한 구독하기 2 - 네이티브 쿼리 짜기 (0) | 2023.01.08 |
[Spring Data JPA] JPA 사용한 구독하기 1 - DB테이블 설정 (0) | 2023.01.08 |
댓글