[SpringBoot] Multipart 파일 Upload & Download

2023. 9. 4. 06:02
반응형

 

 

이번 포스팅에서는 Spring Boot에서 Request로 Multipart 형식의 File을 받아, 서버 내부 스토리지에 저장하는 방법과, 스토리지에 저장된 파일을 Response로 출력하는 과정을 다루어보도록 하겠다.

 

 

 


업로드 파일 저장 디렉터리 생성

 

▶ Application.java

 

@SpringBootApplication
public class WisefeeApplication {

	@Value("${upload.directory}")
	private String uploadDirectory;


	public static void main(String[] args) {
		SpringApplication.run(WisefeeApplication.class, args);
	}

	@PostConstruct
	public void init() {
		File directory = new File(uploadDirectory);
		if (!directory.exists()) {
			directory.mkdirs(); // 디렉터리 생성
		}
	}

}

 

우선 업로드한 파일을 서버 내부 스토리지에 저장하기 위해서, App 실행 시 디렉터리를 생성해주는 로직을 추가해준다.

 

 

 

 

 


파일 업로드

 

 

▶ FileApiController.java

@RestController
@RequestMapping("/api/v1/file")
@RequiredArgsConstructor
public class FileApiController {
    private final FileService fileService;

    @PostMapping(value = "",
            consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Long> addFile(
            @ApiParam(value = "multipart/form-data 형식의 이미지")
            @RequestPart("multipartFile")
            MultipartFile file) {
        Long memberPK = SecurityUtil.getCurrentMemberPk();
        FileInfoDto fileinfo = fileService.uploadFile(file);	//서버 내부 스토리지 저장
        Long success = fileService.insertFileInfo(fileinfo, memberPK);	//데이터베이스에 파일 정보 저장

        return ResponseEntity.status(HttpStatus.OK).body(success);
    }
}

컨트롤러에서 Multipart 파일을 Request로 받으려면 @RequestPart("multipartFile")과 함께 MultipartFile file을 파라미터로 넣어주면 된다.

 

해당 과정은, 받은 Multipart 파일을 서버 내부에 스토리지에 저장하고 그 결과로 나온 파일 정보를 데이터베이스에 저장하는 과정을 거치게 된다.

 

 

 

▶ FileService.java

    /**
     * [파일 업로드]
     * Multipart 파일을 입력받아 서버 내부 스토리지에 저장.
     * @param [MultipartFile 파일]
     * @return [FileinfoDto 파일 정보]
     */
    @Transactional()
    public FileInfoDto uploadFile(MultipartFile file){

        String originalFileName = file.getOriginalFilename();
        String mimeType = file.getContentType();

        //최대용량 체크
        if (file.getSize() > FileConstant.MAX_FILE_SIZE) {
            throw new FileUploadException("10MB 이하 파일만 업로드 할 수 있습니다.");
        }


        //MIMETYPE 체크
        if (!FileUtil.isImageFile(mimeType)) {
            throw new FileUploadException("이미지 파일만 업로드할 수 있습니다.");
        }

        //저장 파일명을 중복방지 고유명으로 변경
        String newFileName = generateUniqueFileName(originalFileName);
        Path filePath = Paths.get(uploadDirectory + File.separator + newFileName);


        //서버 내부 스토리지에 업로드
        try {
            Files.copy(file.getInputStream(), filePath);
        } catch (IOException e) {
            throw new FileUploadException("File upload exception. " + e.getStackTrace());
        }

        return new FileInfoDto(file.getContentType(),
                file.getOriginalFilename(),
                filePath.toString(),
                Long.toString(file.getSize()));
    }


    /**
     * [중복방지를 위한 파일 고유명 생성]
     * @param fileExtension 확장자
     * @return String 파일 고유이름
     */
    private String generateUniqueFileName(String originalFileName) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        // Random 객체 생성
        Random random = new Random();
        // 0 이상 100 미만의 랜덤한 정수 반환
        String randomNumber = Integer.toString(random.nextInt(Integer.MAX_VALUE));
        String timeStamp = dateFormat.format(new Date());
        return timeStamp + randomNumber + originalFileName;
    }

실제로 서버 내부 스토리지에 파일 업로드를 하는 과정이다.


파일 정보 추출: 업로드된 파일에서 원본 파일 이름(originalFileName)과 파일의 MIME 타입(mimeType)을 추출한다.

용량 및 MIME 타입 체크: 파일의 크기가 특정 용량 제한(FileConstant.MAX_FILE_SIZE)을 초과하는지 및 MIME 타입이 이미지인지 여부를 검사하여. 파일 크기가 제한을 초과하면 예외를 발생시키고, 이미지 파일이 아니면 예외를 발생시킨다.

고유한 파일 이름 생성: 중복된 파일 이름을 방지하기 위해 고유한 파일 이름을 생성한다.

파일 저장: 서버 내부 스토리지에 업로드된 파일을 저장한다. 이때, Files.copy 메서드를 사용하여 클라이언트로부터 받은 파일 내용을 서버에 저장한다.

파일 정보 반환: 업로드된 파일의 정보를 담은 FileInfoDto 객체를 생성하여 반환한다. 이 객체에는 MIME 타입, 원본 파일 이름, 저장된 파일 경로, 파일 크기 등의 정보가 포함된다.

 

 

 

 


파일 다운로드

 

▶ FileApiController.java

@RestController
@RequestMapping("/api/v1/file")
@RequiredArgsConstructor
public class FileApiController {
    private final FileService fileService;

    @PostMapping(value = "",
            consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public ResponseEntity<Long> addFile(
            //...
    }


    @GetMapping("/{id}")
    public ResponseEntity<byte[]> getImage(@PathVariable Long id){
        // ID를 통해 이미지 파일의 경로를 얻어옴
        FileInfoDto info = this.fileService.getImageInfoById(id);
        Path imagePath = Paths.get(info.getFilePath());
        String mimeType = info.getFileType();

        // 이미지 파일을 바이트 배열로 읽어옴
        byte[] imageBytes = this.fileService.getImageFile(imagePath);

        return ResponseEntity.status(HttpStatus.OK)
                .contentType(MediaType.valueOf(mimeType))  // 이미지 타입에 맞게 설정
                .body(imageBytes);

    }



}

업로드한 파일은 경로와 MIMETYPE 등을 포함하여 데이터베이스에 저장되어 있는 상태이다.

다음은 저장한 이미지를 PK를 통해서 파일정보를 검색한 후 이미지 자체를 Response로 반환하는 컨트롤러를 작성한 내용이다.

 

 

 

 

▶ FileService.java

    /**
     * [경로를 기반으로 이미지 바이트 스트림 반환]
     * 해당 경로의 이미지의 바이트 스트림 형태를 얻음
     * @param [Path 파일 경로]
     * @return [byte[] 이미지 바이트 배열]
     */
    @Transactional
    public byte[] getImageFile(Path path){
        try {
            byte[] imageBytes = Files.readAllBytes(path);
            return imageBytes;
        } catch (IOException e) {
            throw new FileDownloadException("File download fail." + e.getMessage());
        }
    }


이미지 파일 읽기: 주어진 파일 경로를 사용하여 해당 이미지 파일을 읽어와서 바이트 배열(byte[] imageBytes)로 변환한다.

Files.readAllBytes를 사용하여 이미지 파일을 읽어오면 이미지 데이터가 바이트 배열로 저장된다.

바이트 배열 반환: 이미지 파일을 읽고 바이트 배열로 저장한 후, 이 바이트 배열을 반환한다.


 

 

 


결과 확인

 

이미지 파일 업로드

 

 

 

 

 

이미지 파일 다운로드

반응형

BELATED ARTICLES

more