Skip to content

Commit

Permalink
Merge pull request #24 from SOPT-all/feat/22
Browse files Browse the repository at this point in the history
[FEAT] 게시물 사진 S3 업로드
  • Loading branch information
airoca authored Jan 16, 2025
2 parents 7c71c26 + 0e0f1be commit 008a6e7
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -1,30 +1,58 @@
package com.spoony.spoony_server.domain.post.controller;

import com.spoony.spoony_server.common.dto.ResponseDTO;
import com.spoony.spoony_server.domain.post.dto.PostCreateDTO;
import com.spoony.spoony_server.domain.post.dto.request.PostCreateRequestDTO;
import com.spoony.spoony_server.domain.post.dto.response.PostResponseDTO;
import com.spoony.spoony_server.domain.post.service.PostService;
import com.spoony.spoony_server.infra.service.AwsFileService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.List;

@RestController
@RequestMapping("/api/post")
@RequiredArgsConstructor
public class PostController {

private final PostService postService;
private final AwsFileService awsFileService;

@GetMapping("/{postId}")
public ResponseEntity<ResponseDTO<PostResponseDTO>> getPost(@PathVariable Long postId) {
PostResponseDTO postResponse = postService.getPostById(postId);
return ResponseEntity.status(HttpStatus.OK).body(ResponseDTO.success(postResponse));
}

@PostMapping
public ResponseEntity<ResponseDTO<Void>> createPost(@RequestBody PostCreateRequestDTO postCreateRequestDTO) {
postService.createPost(postCreateRequestDTO);
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<ResponseDTO<Void>> createPost(
@RequestPart("data") PostCreateRequestDTO postCreateRequestDTO,
@RequestPart("photos") List<MultipartFile> photos
) throws IOException {
List<String> photoUrlList = awsFileService.savePostImages(photos);

PostCreateDTO updatedPostCreateDTO = new PostCreateDTO(
postCreateRequestDTO.userId(),
postCreateRequestDTO.title(),
postCreateRequestDTO.description(),
postCreateRequestDTO.placeName(),
postCreateRequestDTO.placeAddress(),
postCreateRequestDTO.placeRoadAddress(),
postCreateRequestDTO.latitude(),
postCreateRequestDTO.longitude(),
postCreateRequestDTO.categoryId(),
postCreateRequestDTO.menuList(),
photoUrlList
);

postService.createPost(updatedPostCreateDTO);

return ResponseEntity.status(HttpStatus.OK).body(ResponseDTO.success(null));
return ResponseEntity.ok(ResponseDTO.success(null));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.spoony.spoony_server.domain.post.dto;

import java.util.List;

public record PostCreateDTO(Long userId,
String title,
String description,
String placeName,
String placeAddress,
String placeRoadAddress,
Double latitude,
Double longitude,
Long categoryId,
List<String> menuList,
List<String> photoUrlList) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,5 @@ public record PostCreateRequestDTO(Long userId,
Double latitude,
Double longitude,
Long categoryId,
List<String> menuList,
String photo) {
// photo 임시 String으로 설정
List<String> menuList) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import com.spoony.spoony_server.common.message.UserErrorMessage;
import com.spoony.spoony_server.domain.place.entity.PlaceEntity;
import com.spoony.spoony_server.domain.place.repository.PlaceRepository;
import com.spoony.spoony_server.domain.post.dto.request.PostCreateRequestDTO;
import com.spoony.spoony_server.domain.post.dto.PostCreateDTO;
import com.spoony.spoony_server.domain.post.dto.response.PostResponseDTO;
import com.spoony.spoony_server.domain.post.entity.*;
import com.spoony.spoony_server.domain.post.repository.*;
Expand All @@ -21,10 +21,12 @@
import com.spoony.spoony_server.domain.user.entity.RegionEntity;
import com.spoony.spoony_server.domain.user.entity.UserEntity;
import com.spoony.spoony_server.domain.user.repository.UserRepository;
import com.spoony.spoony_server.infra.service.AwsFileService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
Expand All @@ -47,11 +49,14 @@ public class PostService {
private final FollowRepository followRepository;
private final FeedRepository feedRepository;

private final AwsFileService awsFileService;

@Transactional
public PostResponseDTO getPostById(Long postId) {

PostEntity postEntity = postRepository.findById(postId).orElseThrow(() -> new BusinessException(PostErrorMessage.NOT_FOUND_ERROR));
UserEntity userEntity = postEntity.getUser();

if (userEntity == null) {
throw new BusinessException(PostErrorMessage.NOT_FOUND_ERROR);
}
Expand All @@ -64,8 +69,7 @@ public PostResponseDTO getPostById(Long postId) {
PlaceEntity place = postEntity.getPlace();
LocalDateTime latestDate = postEntity.getUpdatedAt().isAfter(postEntity.getCreatedAt()) ? postEntity.getUpdatedAt() : postEntity.getCreatedAt();
String formattedDate = latestDate.toLocalDate().toString();
Long zzim_count = postRepository.countByPostId(postId);
//List<String> category_list = List.of(categoryEntity.getCategoryName());
Long zzimCount = postRepository.countByPostId(postId);
String category = categoryEntity.getCategoryName();

List<MenuEntity> menuEntityList = menuRepository.findByPost(postEntity).orElseThrow(() -> new BusinessException(PostErrorMessage.NOT_FOUND_ERROR));
Expand All @@ -74,36 +78,49 @@ public PostResponseDTO getPostById(Long postId) {
.map(menuEntity -> menuEntity.getMenuName())
.collect(Collectors.toList());

return new PostResponseDTO(postId, userEntity.getUserId(), userEntity.getUserName(), regionEntity.getRegionName(), category, postEntity.getTitle(), formattedDate, menuList, postEntity.getDescription(),
place.getPlaceName(), place.getPlaceAddress(), place.getLatitude(), place.getLongitude(), zzim_count
return new PostResponseDTO(
postId,
userEntity.getUserId(),
userEntity.getUserName(),
regionEntity.getRegionName(),
category,
postEntity.getTitle(),
formattedDate,
menuList,
postEntity.getDescription(),
place.getPlaceName(),
place.getPlaceAddress(),
place.getLatitude(),
place.getLongitude(),
zzimCount
);
}

@Transactional
public void createPost(PostCreateRequestDTO postCreateRequestDTO) {
public void createPost(PostCreateDTO postCreateDTO) throws IOException {

// 게시글 업로드
UserEntity userEntity = userRepository.findById(postCreateRequestDTO.userId())
UserEntity userEntity = userRepository.findById(postCreateDTO.userId())
.orElseThrow(() -> new BusinessException(UserErrorMessage.NOT_FOUND_ERROR));

CategoryEntity categoryEntity = categoryRepository.findById(postCreateRequestDTO.categoryId())
CategoryEntity categoryEntity = categoryRepository.findById(postCreateDTO.categoryId())
.orElseThrow(() -> new BusinessException(CategoryErrorMessage.NOT_FOUND_ERROR));

PlaceEntity placeEntity = PlaceEntity.builder()
.placeName(postCreateRequestDTO.placeName())
.placeAddress(postCreateRequestDTO.placeAddress())
.placeRoadAddress(postCreateRequestDTO.placeRoadAddress())
.latitude(postCreateRequestDTO.latitude())
.longitude(postCreateRequestDTO.longitude())
.placeName(postCreateDTO.placeName())
.placeAddress(postCreateDTO.placeAddress())
.placeRoadAddress(postCreateDTO.placeRoadAddress())
.latitude(postCreateDTO.latitude())
.longitude(postCreateDTO.longitude())
.build();

placeRepository.save(placeEntity);

PostEntity postEntity = PostEntity.builder()
.user(userEntity)
.place(placeEntity)
.title(postCreateRequestDTO.title())
.description(postCreateRequestDTO.description())
.title(postCreateDTO.title())
.description(postCreateDTO.description())
.createdAt(LocalDateTime.now())
.updatedAt(LocalDateTime.now())
.build();
Expand All @@ -117,19 +134,19 @@ public void createPost(PostCreateRequestDTO postCreateRequestDTO) {

postCategoryRepository.save(postCategoryEntity);

postCreateRequestDTO.menuList().stream()
postCreateDTO.menuList().stream()
.map(menuName -> MenuEntity.builder()
.post(postEntity)
.menuName(menuName)
.build())
.forEach(menuRepository::save);

PhotoEntity photoEntity = PhotoEntity.builder()
.post(postEntity)
.photoUrl(postCreateRequestDTO.photo())
.build();

photoRepository.save(photoEntity);
postCreateDTO.photoUrlList().stream()
.map(photoUrl -> PhotoEntity.builder()
.post(postEntity)
.photoUrl(photoUrl)
.build())
.forEach(photoRepository::save);

// 작성자 스푼 개수 조정
ActivityEntity activityEntity = activityRepository.findById(2L)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.spoony.spoony_server.infra.service;

import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import com.spoony.spoony_server.common.exception.BusinessException;
Expand All @@ -17,6 +16,8 @@
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

Expand All @@ -34,28 +35,36 @@ public class AwsFileService {
private String PROFILE_IMG_DIR = "profile/";
private String POST_IMG_DIR = "post/";

public String savePhoto(MultipartFile multipartFile, Long memberId) throws IOException {
public String saveProfileImage(MultipartFile multipartFile) throws IOException {
File uploadFile = convert(multipartFile)
.orElseThrow(() -> new BusinessException(S3ErrorMessage.FILE_CHANGE_FAIL));
return upload(uploadFile, POST_IMG_DIR, memberId);
return upload(uploadFile, PROFILE_IMG_DIR);
}

public String saveProfileImg(MultipartFile multipartFile, Long memberId) throws IOException {
public String savePostImage(MultipartFile multipartFile) throws IOException {
File uploadFile = convert(multipartFile)
.orElseThrow(() -> new BusinessException(S3ErrorMessage.FILE_CHANGE_FAIL));
return upload(uploadFile, PROFILE_IMG_DIR, memberId);
return upload(uploadFile, POST_IMG_DIR);
}

private String upload(File uploadFile, String dirName, Long memberId) {
String fileName = dirName + memberId + "/" + UUID.randomUUID() + uploadFile.getName(); // S3에 저장된 파일 이름
public List<String> savePostImages(List<MultipartFile> photos) throws IOException {
List<String> photoUrls = new ArrayList<>();
for (MultipartFile photo : photos) {
String photoUrl = savePostImage(photo); // 개별 파일 업로드
photoUrls.add(photoUrl);
}
return photoUrls;
}

private String upload(File uploadFile, String dirName) {
String fileName = dirName + "/" + UUID.randomUUID() + uploadFile.getName(); // S3에 저장된 파일 이름
String uploadImageUrl = putS3(uploadFile, fileName);
removeNewFile(uploadFile);
return uploadImageUrl;
}

private String putS3(File uploadFile, String fileName) {
amazonS3Client.putObject(new PutObjectRequest(bucket, fileName, uploadFile).withCannedAcl(
CannedAccessControlList.PublicRead));
amazonS3Client.putObject(new PutObjectRequest(bucket, fileName, uploadFile));
return amazonS3Client.getUrl(bucket, fileName).toString();
}

Expand All @@ -68,7 +77,15 @@ private void removeNewFile(File targetFile) {
}

private Optional<File> convert(MultipartFile file) throws IOException {
File convertFile = new File(System.getProperty("user.home") + "/" + file.getOriginalFilename());
String photosDir = "src/main/resources/photos/";

File directory = new File(photosDir);
if (!directory.exists()) {
directory.mkdirs();
}

File convertFile = new File(photosDir + file.getOriginalFilename());

if (convertFile.createNewFile()) {
try (FileOutputStream fos = new FileOutputStream(convertFile)) {
fos.write(file.getBytes());
Expand Down

0 comments on commit 008a6e7

Please sign in to comment.