게시물 포스팅 시 이미지를 첨부파일로 업로드하는 기능을 구현해보았다.
첨부 파일 Entity
@Data
@Entity
public class Attachment {
public Attachment() {
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String originName;
private String storeName;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id")
private Post post;
public Attachment(String originName, String storeName) {
this.originName = originName;
this.storeName = storeName;
}
}
첨부파일의 원본 이름과 저장할 이름을 변수로 갖도록 작성하였고 첨부파일과 게시물이 다대일 연관관계가 되도록 @ManyToOne으로 묶어주었다.
AttachmentAddForm
@Data
public class AttachmentAddForm {
private List<MultipartFile> imageFiles;
}
엔티티에 파일을 직접 저장하는 것이 아닌 원본명과 저장된 이름을 저장할 것이기 때문에 파일을 전달할 목적으로 AttachmentAddForm을 만든다.
Repository
public interface AttachmentRepository extends JpaRepository<Attachment, Long> {
}
기본적인 CRUD를 사용할 것임으로 JPA Repository를 상속 받아 사용하였다.
FileStore(Service)
@Service
@RequiredArgsConstructor
@Slf4j
public class FileStore {
private final AttachmentRepository attachmentRepository;
@Value("${file.dir}")
private String fileDir;
public void storeFiles(List<MultipartFile> multipartFiles, Post post) throws IOException {
List<Attachment> attachments = new ArrayList<>();
for (MultipartFile multipartFile : multipartFiles) {
if (!multipartFiles.isEmpty()) {
Attachment attachment = storeFile(multipartFile);
attachment.setPost(post);
attachments.add(attachment);
Attachment saved = attachmentRepository.save(attachment);
log.info("이미지파일 ID ={}", saved.getId());
}
}
}
public String getFullPath(String fileName) {
return fileDir + fileName;
}
public Attachment storeFile(MultipartFile multipartFile) throws IOException {
if (multipartFile.isEmpty()) {
return null;
}
String originalFilename = multipartFile.getOriginalFilename();
String storeFileName = createFileName(originalFilename);
multipartFile.transferTo(new File(getFullPath(storeFileName)));
return new Attachment(originalFilename, storeFileName);
}
private String createFileName(String originalFilename) {
String uu = UUID.randomUUID().toString();
String extracted = extract(originalFilename);
String storeFileName = uu + "." + extracted;
return storeFileName;
}
private String extract(String originalFilename) {
int pos = originalFilename.lastIndexOf(".");
String ext = originalFilename.substring(pos + 1);
return ext;
}
}
서비스에서의 메서드는 크게 3가지로 구성하였다.
- 확장자를 분리해오는 extract 메서드
- 파일명의 중복 방지를 위해 UUID로 새로운 이름을 생성하는 createFileName 메서드
- Requst로 받은 MultipartFile을 특정 경로에 저장하고 attachment 객체를 만드는 storeFile 메서드 -> 여기서 multipartFile이 Attachment객체로 바뀌어 생성된다.
그리고 위 3 메서드를 활용하는 메서드
- List로 여러 MultipartFile이 들어왔을때 하나씩 꺼내 storeFile메서드를 수행하도록 하는 void storeFiles 메서드
- 저장된 파일명을 포함한 모든 경로명을 return 하는 getFullPath 메서드를 만들었다.
application.properties에 아래와 같이 설정해주면
file.dir= /C:/Users/사용자명/Downloads/imageFile/
@Value(${flie.dir}) 를 사용하여 fileDir이라는 변수에 해당 경로값을 지정 할 수 있다.
MultiFile
MultiFile클래스의 경우 getOriginalFileName을 제공해준다.
아래 코드를 통해 특정 경로/ 파일명으로 해당 파일을 저장하도록 한다.
- multipartFile.transferTo(new File(getFullPath(storeFileName)));
Controller(게시물 등록)
@GetMapping("/posts/add")
public String addForm(@ModelAttribute Post post, @ModelAttribute AttachmentAddForm attachmentForm) {
return "post/addForm";
}
@PostMapping("/posts/add")
public String addPost(@Valid @ModelAttribute Post post, @ModelAttribute AttachmentAddForm attachmentForm, BindingResult bindingResult, RedirectAttributes redirectAttributes, @AuthenticationPrincipal PrincipalDetail principalDetail) throws IOException {
if (bindingResult.hasErrors()) {
log.info("error={}", bindingResult);
return "post/addForm";
}
Post savedPost = postService.save(post, principalDetail.getMember());
redirectAttributes.addAttribute("postId", savedPost.getId());
redirectAttributes.addAttribute("status", true);
List<MultipartFile> imageFiles = attachmentForm.getImageFiles();
fileStore.storeFiles(imageFiles, post);
return "redirect:/post/{postId}";
}
PostDTO를 만들어 이 객체에 MultipartFile을 담아서 전달해도 된다. 나의 경우는 이미 Post객체로 저장하도록 만들어 놓은 코드가 있어서 모두 수정하기엔 복잡했기에 AttachmentAddForm을 모델로 받아 받은 데이터를 Attachment객체에 저장하도록 컨트롤러를 짰다.
Controller(이미지 페이지)
@ResponseBody
@GetMapping("/images/{filename}")
public Resource downloadImage(@PathVariable String filename) throws MalformedURLException {
return new UrlResource("file:" + fileStore.getFullPath(filename));
}
위 코드는 파일명에 해당하는 URL을 생성하여 보여주는 컨트롤러이다. 이미지 파일을 저장하면 해당 이미지 파일의 이름을 파라미터로 URL을 생성하도록 하였다.
'커뮤니티 프로젝트' 카테고리의 다른 글
좋아요 List up 구현 (0) | 2022.12.13 |
---|---|
좋아요 기능 개발 (0) | 2022.12.12 |
댓글 삭제 개발 (0) | 2022.12.07 |
댓글 기능 개발 (댓글 저장 부분) (0) | 2022.12.02 |
회원정보 수정 (0) | 2022.11.30 |