Published 2022. 8. 9. 17:55
이전글 보기
https://aamoos.tistory.com/685
목표
- 저번장까지 파일업로드를 개발하였는데, 이번장에서는 등록한 게시판, 파일테이블을 이어주는 중간 테이블을 만들어보겠습니다.
BoardFile.java
package jpa.board.entity;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
/**
* packageName : jpa.board.entity
* fileName : BoardFile
* author : 김재성
* date : 2022-08-09
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2022-08-09 김재성 최초 생성
*/
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@EntityListeners(AuditingEntityListener.class)
public class BoardFile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "board_file_id")
private Long id; //번호
private Long boardId;
private String delYn;
@OneToOne
@JoinColumn(name = "file_id")
private File file;
@Builder
public BoardFile(Long boardId, Long fileId, String delYn, File file){
this.boardId = boardId;
this.delYn = "N";
this.file = file;
}
}
- File과 OneToOne 연관관계를 추가하였습니다.
File.java
package jpa.board.entity;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.time.LocalDateTime;
/**
* packageName : jpa.board.entity
* fileName : File
* author : 김재성
* date : 2022-08-05
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2022-08-05 김재성 최초 생성
*/
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@EntityListeners(AuditingEntityListener.class)
public class File {
@Id
@GeneratedValue
@Column(name = "file_id")
private Long id; //id
@Column(nullable = false)
private String originFileName; //원본 파일명
@Column(nullable = false)
private String savedFileName; //저장된 파일명
private String uploadDir; //경로명
private String extension; //확장자
private Long size; //파일 사이즈
private String contentType; //ContentType
@CreatedDate
private LocalDateTime regDate; //등록 날짜
@OneToOne(mappedBy = "file")
private BoardFile boardFile;
@Builder
public File(Long id, String originFileName, String savedFileName
, String uploadDir, String extension, Long size, String contentType){
this.id = id;
this.originFileName = originFileName;
this.savedFileName = savedFileName;
this.uploadDir = uploadDir;
this.extension = extension;
this.size = size;
this.contentType = contentType;
}
}
- BoardFile과 OneToOne 연관관계를 추가하였습니다.
BoardFileDto.java
package jpa.board.dto;
import com.querydsl.core.annotations.QueryProjection;
import jpa.board.entity.Board;
import jpa.board.entity.BoardFile;
import jpa.board.entity.File;
import lombok.Builder;
import lombok.Data;
import java.time.LocalDateTime;
/**
* packageName : jpa.board.dto
* fileName : BoardFileDto
* author : 김재성
* date : 2022-08-09
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2022-08-09 김재성 최초 생성
*/
@Data
public class BoardFileDto {
private Long id;
private Long boardId;
public BoardFileDto(){
}
@Builder
public BoardFileDto(Long boardId){
this.boardId = boardId;
}
public BoardFile toEntity(File file){
return BoardFile.builder()
.boardId(boardId)
.file(file)
.build();
}
}
- entity로 바꿔줄때 file을 받는 부분을 추가하였습니다.
FileService.java
package jpa.board.service;
import jpa.board.dto.BoardDto;
import jpa.board.dto.BoardFileDto;
import jpa.board.dto.FileDto;
import jpa.board.entity.BoardFile;
import jpa.board.entity.Member;
import jpa.board.repository.BoardFileRepository;
import jpa.board.repository.FileRepository;
import jpa.board.repository.MemberRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.InputStream;
import java.util.*;
/**
* packageName : jpa.board.service
* fileName : FileService
* author : 김재성
* date : 2022-08-05
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2022-08-05 김재성 최초 생성
*/
@Service
@RequiredArgsConstructor
@Slf4j
public class FileService {
@Value("${upload.path}")
private String uploadDir;
private final FileRepository fileRepository;
private final BoardFileRepository boardFileRepository;
private final MemberRepository memberRepository;
@Transactional
public Map<String, Object> saveFile(BoardDto boardDto, Long boardId) throws Exception {
List<MultipartFile> multipartFile = boardDto.getMultipartFile();
//결과 Map
Map<String, Object> result = new HashMap<String, Object>();
//파일 시퀀스 리스트
List<Long> fileIds = new ArrayList<Long>();
try {
if (multipartFile != null) {
if (multipartFile.size() > 0 && !multipartFile.get(0).getOriginalFilename().equals("")) {
for (MultipartFile file1 : multipartFile) {
String originalFileName = file1.getOriginalFilename(); //오리지날 파일명
String extension = originalFileName.substring(originalFileName.lastIndexOf(".")); //파일 확장자
String savedFileName = UUID.randomUUID() + extension; //저장될 파일 명
File targetFile = new File(uploadDir + savedFileName);
//초기값으로 fail 설정
result.put("result", "FAIL");
FileDto fileDto = FileDto.builder()
.originFileName(originalFileName)
.savedFileName(savedFileName)
.uploadDir(uploadDir)
.extension(extension)
.size(file1.getSize())
.contentType(file1.getContentType())
.build();
//파일 insert
jpa.board.entity.File file = fileDto.toEntity();
Long fileId = insertFile(file);
log.info("fileId={}", fileId);
try {
InputStream fileStream = file1.getInputStream();
FileUtils.copyInputStreamToFile(fileStream, targetFile); //파일 저장
//배열에 담기
fileIds.add(fileId);
result.put("fileIdxs", fileIds.toString());
result.put("result", "OK");
} catch (Exception e) {
//파일삭제
FileUtils.deleteQuietly(targetFile); //저장된 현재 파일 삭제
e.printStackTrace();
result.put("result", "FAIL");
break;
}
BoardFileDto boardFileDto = BoardFileDto.builder()
.boardId(boardId)
.build();
BoardFile boardFile = boardFileDto.toEntity(file);
insertBoardFile(boardFile);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/** 파일 저장 db */
@Transactional
public Long insertFile(jpa.board.entity.File file) {
return fileRepository.save(file).getId();
}
@Transactional
public Long insertBoardFile(BoardFile boardFile) {
return boardFileRepository.save(boardFile).getId();
}
}
- insertBoardFile 메소드를 추가하였습니다. boardFile은 id만 받고, 삭제여부는 N으로 고정하였습니다. boardFileDto를 entity로 변환할때 전에 저장한 file entity를 설정해주고 boardFile을 저장하는 방식으로 개발하였습니다.
BoardFileRepository.java
package jpa.board.repository;
import jpa.board.entity.Board;
import jpa.board.entity.BoardFile;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* packageName : jpa.board.repository
* fileName : BoardFileRepository
* author : 김재성
* date : 2022-08-09
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2022-08-09 김재성 최초 생성
*/
public interface BoardFileRepository extends JpaRepository<BoardFile, Long> {
}
결과화면
게시판 테이블
파일 테이블
게시판 첨부파일 테이블
파일
- 게시판 등록을 해보면 테이블 3개로 나뉘어 등록이 되는것을 볼수있습니다. BOARD_FILE 테이블을 통해 1번 게시물에 2번 3번 파일이 등록된것을 알수 있습니다. 다음장에서는 등록한 파일을 상세에서 다운로드 할수 있는 기능을 개발해보겠습니다.
다음글 보기
https://aamoos.tistory.com/688