Skip to content

Commit

Permalink
Image 업로드 및 렌더링
Browse files Browse the repository at this point in the history
  • Loading branch information
youjungHwang committed Dec 14, 2022
1 parent fc5b9c4 commit 12cdf61
Show file tree
Hide file tree
Showing 17 changed files with 278 additions and 41 deletions.
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

implementation 'org.springframework.boot:spring-boot-starter-security'
// 타임리프에서 스프링시큐리티의 문법이나 형식을 지원하는 확장팩 라이브러리
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5'

compileOnly 'org.projectlombok:lombok'
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/com/photo/config/WebMvcConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.photo.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.PathResourceResolver;

@Component
public class WebMvcConfig implements WebMvcConfigurer { // web 설정 파일

@Value("${file.path}")
private String imageUploadRoute;

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
WebMvcConfigurer.super.addResourceHandlers(registry);

registry
.addResourceHandler("/upload/**")
.addResourceLocations("file:///"+imageUploadRoute)
.setCachePeriod(60*10*6)
.resourceChain(true)
.addResolver(new PathResourceResolver());
}

}
34 changes: 34 additions & 0 deletions src/main/java/com/photo/domain/image/Image.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.photo.domain.image;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.photo.domain.BaseTimeEntity;
import com.photo.domain.user.User;
import lombok.*;

import javax.persistence.*;

@NoArgsConstructor
@Getter
@Entity
public class Image extends BaseTimeEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

private String caption;
private String imageUrl;

@JoinColumn(name = "userId")
@ManyToOne
private User user;

@Builder
public Image(String caption, String imageUrl, User user) {
this.caption = caption;
this.imageUrl = imageUrl;
this.user = user;
}



}
6 changes: 6 additions & 0 deletions src/main/java/com/photo/domain/image/ImageRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.photo.domain.image;

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

public interface ImageRepository extends JpaRepository<Image, Integer> {
}
8 changes: 8 additions & 0 deletions src/main/java/com/photo/domain/user/User.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.photo.domain.user;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.photo.domain.BaseTimeEntity;
import com.photo.domain.image.Image;
import lombok.*;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.List;

@Builder
@AllArgsConstructor
Expand Down Expand Up @@ -34,4 +37,9 @@ public class User extends BaseTimeEntity {
private String role;


@OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
@JsonIgnoreProperties({"user"}) // 응답시 무한 참조 막기위함, image 엔티티에 있는 user는 무시하고 JSON 파싱한다.
private List<Image> images;


}
12 changes: 11 additions & 1 deletion src/main/java/com/photo/handler/ControllerExceptionHandler.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.photo.handler;

import com.photo.handler.exception.CustomApiException;
import com.photo.handler.exception.CustomException;
import com.photo.handler.exception.CustomValidationApiException;
import com.photo.handler.exception.CustomValidationException;
import com.photo.util.Popup;
Expand All @@ -18,7 +19,16 @@ public class ControllerExceptionHandler {

@ExceptionHandler(CustomValidationException.class)
public String validationException(CustomValidationException e){
return Popup.historyBack(e.getErrors().toString());
if(e.getErrors() == null){
return Popup.historyBack(e.getMessage());
}else{
return Popup.historyBack(e.getErrors().toString());
}
}

@ExceptionHandler(CustomException.class)
public String exception(CustomException e){
return Popup.historyBack(e.getMessage());
}

@ExceptionHandler(CustomValidationApiException.class)
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/com/photo/handler/exception/CustomException.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.photo.handler.exception;

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

import java.util.Map;

public class CustomException extends RuntimeException{

private static final long serialVersionUID = 1L;

public CustomException(String message) {
super(message);
}

}
43 changes: 43 additions & 0 deletions src/main/java/com/photo/service/ImageService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.photo.service;

import com.photo.config.auth.CustomUserDetails;
import com.photo.domain.image.Image;
import com.photo.domain.image.ImageRepository;
import com.photo.web.dto.image.ImageUploadDto;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;

@RequiredArgsConstructor
@Service
public class ImageService {

private final ImageRepository imageRepository;

@Value("${file.path}")
private String imageUploadRoute;

@Transactional
public void imageUpload(CustomUserDetails customUserDetails, ImageUploadDto imageUploadDto) {
UUID uuid = UUID.randomUUID();
String imageFileName = uuid+"_"+imageUploadDto.getFile().getOriginalFilename();

Path imageFilePath = Paths.get(imageUploadRoute+imageFileName);

try{
Files.write(imageFilePath, imageUploadDto.getFile().getBytes());
}catch (Exception e){
e.printStackTrace();
}

Image image = imageUploadDto.toEntity(imageFileName, customUserDetails.getUser());
imageRepository.save(image);

}
}
21 changes: 21 additions & 0 deletions src/main/java/com/photo/service/UserService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,41 @@

import com.photo.domain.user.User;
import com.photo.domain.user.UserRepository;
import com.photo.handler.exception.CustomException;
import com.photo.handler.exception.CustomValidationApiException;
import com.photo.web.dto.user.UserProfileDto;
import com.photo.web.dto.user.UserUpdateDto;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.function.Supplier;

@RequiredArgsConstructor
@Service
public class UserService {

private final UserRepository userRepository;
private final BCryptPasswordEncoder bCryptPasswordEncoder;


@Transactional(readOnly = true)
public UserProfileDto profile(int pageUserId, int sessionId) {
UserProfileDto dto = new UserProfileDto();

User userEntity = userRepository.findById(pageUserId).orElseThrow(
() -> new CustomException("해당 profile 페이지는 없는 페이지입니다."));

dto.setUser(userEntity);
dto.setPageUserState(pageUserId == sessionId);
dto.setImageCount(userEntity.getImages().size());


return dto;
}


@Transactional
public User userUpdate(int id, User userUpdateDto){
User userEntity = userRepository.findById(id).orElseThrow(()-> new CustomValidationApiException("해당 유저를 찾을 수 없습니다."));
Expand Down
24 changes: 22 additions & 2 deletions src/main/java/com/photo/web/ImageController.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,45 @@
package com.photo.web;

import com.photo.config.auth.CustomUserDetails;
import com.photo.handler.exception.CustomValidationException;
import com.photo.service.ImageService;
import com.photo.web.dto.image.ImageUploadDto;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@RequiredArgsConstructor
@Controller
public class ImageController {

private final ImageService imageService;

@GetMapping({"/","/image/story"})
public String story() {
return "image/story";
}

@GetMapping({"/image/popular"})
@GetMapping("/image/popular")
public String popular() {
return "image/popular";
}

@GetMapping({"/image/upload"})
@GetMapping("/image/upload")
public String upload() {
return "image/upload";
}

@PostMapping("/image")
public String imageUpload(@AuthenticationPrincipal CustomUserDetails customUserDetails, ImageUploadDto imageUploadDto) {
if(imageUploadDto.getFile().isEmpty()) {
throw new CustomValidationException("이미지가 첨부되지 않았습니다.",null);
}

imageService.imageUpload(customUserDetails, imageUploadDto);
return "redirect:/user/"+customUserDetails.getUser().getId();
}

}
14 changes: 12 additions & 2 deletions src/main/java/com/photo/web/UserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import com.photo.config.auth.CustomUserDetails;
import com.photo.domain.user.User;
import com.photo.service.UserService;
import com.photo.web.dto.user.UserProfileDto;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContextHolder;
Expand All @@ -10,11 +13,18 @@
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@RequiredArgsConstructor
@Controller
public class UserController {

@GetMapping("/user/{id}")
public String profile(@PathVariable int id) {
private final UserService userService;

@GetMapping("/user/{pageUserId}")
public String profile(@PathVariable int pageUserId, Model model, @AuthenticationPrincipal CustomUserDetails customUserDetails) {
UserProfileDto userProfileDto = userService.profile(pageUserId, customUserDetails.getUser().getId());
model.addAttribute("dto", userProfileDto);
model.addAttribute("sessionId", customUserDetails.getUser().getId());

return "user/profile";
}

Expand Down
27 changes: 27 additions & 0 deletions src/main/java/com/photo/web/dto/image/ImageUploadDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.photo.web.dto.image;

import com.photo.domain.image.Image;
import com.photo.domain.user.User;
import lombok.*;
import org.springframework.web.multipart.MultipartFile;

@Data
public class ImageUploadDto {
private MultipartFile file;
private String caption;

@Builder
public ImageUploadDto(MultipartFile file, String caption) {
this.file = file;
this.caption = caption;
}

public Image toEntity(String imageUrl, User user){
return Image.builder()
.caption(caption)
.imageUrl(imageUrl)
.user(user)
.build();

}
}
17 changes: 17 additions & 0 deletions src/main/java/com/photo/web/dto/user/UserProfileDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.photo.web.dto.user;

import com.photo.domain.user.User;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class UserProfileDto {
private boolean pageUserState;
private int imageCount;
private User user;
}
10 changes: 10 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ spring:
hibernate:
format_sql : true
open-in-view: true
servlet:
multipart:
enabled: true
max-file-size: 3MB
max-request-size: 3MB

logging:
level:
Expand All @@ -28,3 +33,8 @@ logging:
# descriptor:
# sql: trace



file:
path: C:/Users/dbwjd/Desktop/Projects/ImageUpload/

Binary file modified src/main/resources/static/images/defaultStory.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/main/resources/templates/image/upload.html
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
<!--사진업로드 로고 end-->

<!--사진업로드 Form-->
<form class="upload-form" >
<form class="upload-form" action="/image" method="post" enctype="multipart/form-data" >
<input type="file" name="file" onchange="imageChoose(this)"/>
<div class="upload-img" style="height: 500px">
<img th:src="@{/images/defaultStory.png}" alt="" id="imageUploadPreview" />
</div>
<br>
<!--사진설명 + 업로드버튼-->
<div class="upload-form-detail">
<input type="text" placeholder="사진설명" name="caption">
<input type="text" placeholder="사진설명" name="caption" required="required"/>
<button class="cta blue">업로드</button>
</div>
<!--사진설명end-->
Expand Down

0 comments on commit 12cdf61

Please sign in to comment.