[Spring] 게시판에 첨부파일 업로드 기능 추가하기

예원·2022년 8월 19일
0

Spring 글 모아보기

목록 보기
4/17
post-custom-banner

간단한 게시판을 구축하는 과정에서 이미지 업로드를 구현했다.

그 과정을 정리하고자 한다.

_~~게시글을 보기전..
해당 코드는 Spring Boot를 공부한지 얼마되지 않아 작성한 글입니다.~~_

1. Gradle 추가

implementation 'commons-fileupload:commons-fileupload:1.4'
implementation 'commons-io:commons-io:2.11.0'
implementation 'org.apache.tika:tika-core:2.4.1'

2. Model 작성

게시물 당 한개의 이미지만 업로드 가능하다는 가정하에 진행하였기에 @OneToOne 관계로 두 테이블을 설정하였다.
attachfileboardbid(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();
    }
}

3. Service 작성

@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);
    }
}

4. Controller 작성

이미지 파일을 받기 위해서 MultipartFile 를 사용했다.
따라서 consumes = {MediaType.MULTIPART_FORM_DATA_VALUE} 를 지정해준다.

BoardMultipartHttpServletRequest 로, fileMultipartFile 으로 받는다.

@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);
    }
}

5. 테스트

Postman을 이용해 테스트 해보면 잘 저장되는 것을 확인할 수 있다.

post-custom-banner

0개의 댓글