간단한 게시판을 구축하는 과정에서 이미지 업로드를 구현했다.
그 과정을 정리하고자 한다.
_~~게시글을 보기전..
해당 코드는 Spring Boot를 공부한지 얼마되지 않아 작성한 글입니다.~~_
implementation 'commons-fileupload:commons-fileupload:1.4'
implementation 'commons-io:commons-io:2.11.0'
implementation 'org.apache.tika:tika-core:2.4.1'
게시물 당 한개의 이미지만 업로드 가능하다는 가정하에 진행하였기에 @OneToOne 관계로 두 테이블을 설정하였다.
attachfile
은 board
의 bid(pk)
를 가진다.
board
@Getter
@NoArgsConstructor
@Entity
@DynamicUpdate
public class Board extends BaseTimeEntity {
@Id
private Long bid;
...
@OneToOne(fetch = FetchType.LAZY, mappedBy = "board")
private Attachfile attachfile;
...
}
attachfile
@Getter
@NoArgsConstructor
@Entity
@DynamicUpdate
public class Attachfile {
@Id
private Long fid;
private String filename;
private String filepath;
@OneToOne
@JoinColumn(name = "bid")
@JsonIgnore
private Board board;
@Builder
public Attachfile(final long fid, String filename, String filepath, Board board){
this.fid = fid;
this.filename = filename;
this.filepath = filepath;
this.board = board;
}
}
FileDTO
파일을 서버에 저장후 결과를 담을 DTO도 생성한다.
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FileDTO {
private String filename; // 파일 원래 이름
private String filepath; // 파일 저장 경로(절대경로로 저장)
private int code; // 파일 저장 결과
// 1(성공), -1(확장자 오류), -2(업로드 오류)
@Builder
public FileDTO(final String filename, String filepath, int code){
this.filename = filename;
this.filepath = filepath;
this.code = code;
}
public static FileDTO res(final String filename, String filepath, int code) {
return FileDTO.builder()
.filename(filename)
.filepath(filepath)
.code(code)
.build();
}
}
@Slf4j
@Service
public class FileService {
@Value("${imageFile.uploadPath}")
private String filePath;
// 허용할 확장자
final List<String> allowFileType = Arrays.asList(".jpg", ".jpeg", ".png");
@Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
multipartResolver.setDefaultEncoding("UTF-8");
multipartResolver.setMaxUploadSizePerFile(10 * 1024 * 1024);
return multipartResolver;
}
public FileDTO uploadFile(MultipartFile file) {
// 파일의 이름, 크기, 확장자
String fileName = file.getOriginalFilename();
long size = file.getSize();
String fileExtension = fileName.substring(fileName.lastIndexOf("."), fileName.length());
// 확장자 검사
int typeChk = 0;
for (String type:allowFileType) {
if(fileExtension.equals(type))
typeChk += 1;
}
if(typeChk < 1) {
return new FileDTO("","",-1);
}
// 저장할 위치 지정
Calendar cal = Calendar.getInstance();
String year = Integer.toString(cal.get(Calendar.YEAR));
String month = String.format("%02d", (cal.get(Calendar.MONTH) + 1));
String day = String.format("%02d", (cal.get(Calendar.DATE)));
String uploadPath = "/" + year + "/" + month + "/" + day + "/";
// 해당 디렉터리가 없는 경우 생성
File dir = new File(filePath, uploadPath);
if(!dir.exists()) {
dir.mkdirs();
}
//저장할 파일 이름, 경로 저장
String newFileName = Long.toString(cal.getTimeInMillis()) + fileExtension;
String newFilePath = uploadPath + newFileName;
//파일 업로드
File saveFile = new File(filePath + newFilePath);
try {
file.transferTo(saveFile);
} catch (IllegalStateException e) {
log.error("파일 업로드 에러");
return new FileDTO("","",-2);
} catch (IOException e) {
log.error("파일 업로드 에러");
return new FileDTO("","",-2);
}
// 결과 전송
return new FileDTO(fileName, newFilePath , 1);
}
}
이미지 파일을 받기 위해서 MultipartFile
를 사용했다.
따라서 consumes = {MediaType.MULTIPART_FORM_DATA_VALUE}
를 지정해준다.
Board
는 MultipartHttpServletRequest
로, file
은 MultipartFile
으로 받는다.
@RestController
@RequestMapping("/community")
public class CommunityController {
@PostMapping(value = "", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity upload(MultipartHttpServletRequest request, @RequestParam(value = "file", required = false) MultipartFile file){
// 1. board 저장
// request 로부터 게시물 내용을 받아온 뒤,
Board board = Board.builder()
.bid(System.currentTimeMillis())
.title(request.getParameter("title"))
.cn(request.getParameter("cn"))
.userName(request.getParameter("userName"))
.build();
// DB에 저장한다.
communityRepository.save(board);
if(file != null) {
// 2. 파일이 있는 경우 서버에 저장
FileDTO filedto = fileService.uploadFile(file);
if (filedto.getCode() == -2) {
return new ResponseEntity(ResponseFmt.res(StatusCode.BAD_REQUEST, ResponseMessage.IMAGE_UPLOAD_ERROR), HttpStatus.OK);
}
if (filedto.getCode() == -1) {
return new ResponseEntity(ResponseFmt.res(StatusCode.BAD_REQUEST, ResponseMessage.CANT_NOT_OTHER_FILES), HttpStatus.OK);
}
// 3. attachfile 저장
Attachfile attachfile = Attachfile.builder()
.fid(System.currentTimeMillis())
.filename(filedto.getFilename())
.filepath(imageServerPath + filedto.getFilepath())
.board(board)
.build();
attachfileRepository.save(attachfile);
}
// 4. 결과 return
return new ResponseEntity(ResponseFmt.res(StatusCode.OK, ResponseMessage.SAVE_NEW_BOARD), HttpStatus.OK);
}
}
Postman을 이용해 테스트 해보면 잘 저장되는 것을 확인할 수 있다.