MinIo Spring Boot에서 사용하기

달달한단밤·2024년 5월 4일
post-thumbnail

MinIO란?

AWS S3와 호환되는 고성능 오브젝트 스토리지 오픈소스

S3와 사용법이 유사하고 S3와 호환가능하다.

1. Docker 컨테이너 띄우기

docker run --user root -d --name minio -e "MINIO_ROOT_USER={사용할 root ID}" -e "MINIO_ROOT_PASSWORD={사용할 root PASSWORD}" -p 4380:9000 -p 4381:9001  -v minio:/data [quay.io/minio/minio](http://quay.io/minio/minio) server /data --console-address ":9001"

2. Minio 접속

서버 주소:4381로 접속

  • 컨테이너 띄울 때 입력한 id password로 로그인

3. KEY 생성

  • 왼쪽 메뉴의 Access Keys 클릭 후 Create access key 클릭

4. Bucket 생성

  • 왼쪽 메뉴에서 Buckets 클릭 후 Create Bucket 클릭

5. Spring Boot 연결

5.1 dependencies 추가

//minio
	implementation 'io.minio:minio:8.5.9'

5.2 MinioCongig.java 생성

package org.sixback.omess.common.config;

import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MinioConfig {
    @Value("${minio.url}") 
    private String url;

    @Value("${minio.access.name}")
    private String accessKey;

    @Value("${minio.access.secret}")
    private String secretKey;

    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(url)
                .credentials(accessKey, secretKey)
                .build();
    }

}

5.3 application.yml 수정

minio:
  url: ${MINIO_URL} //minio 주소
  path: ${MINIO_PATH}
  dir:
    image: ${MINIO_IMAGE_DIR} // 버킷 내 이미지 저장할 폴더 명
  bucket:
    name: ${MINIO_BUCKET}  // 버킷 이름
  access:
    name: ${MINIO_ACCESS_KEY}  //3에서 발급한 access key
    secret: ${MINIO_SECRET_KEY} //3에서 발급한 secretKey key

6. 이미지 업로드 및 삭제 구현

Controller

package org.sixback.omess.domain.image.controller;

import io.minio.errors.*;
import lombok.RequiredArgsConstructor;
import org.sixback.omess.domain.image.service.ImageFileService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

@RestController
@RequestMapping("/api/v1/images")
@RequiredArgsConstructor
public class ImageController {
    private final ImageFileService imageFileService;

    @PostMapping
    public ResponseEntity<String> uploadImage(@RequestBody MultipartFile file) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        return ResponseEntity.ok().body(imageFileService.saveImageFile(file));
    }

    @DeleteMapping
    public ResponseEntity<Void> deleteImage(@RequestBody String fileName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        imageFileService.deleteImage(fileName);

        return ResponseEntity.ok().build();
    }
}

Service

package org.sixback.omess.domain.image.service;

import io.minio.*;
import io.minio.errors.*;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class ImageFileService {
    private final MinioClient minioClient;

    @Value("${minio.bucket.name}")
    private String BUCKET_NAME;

    @Value("${minio.dir.image}")
    private String IMAGE_DIR;

    @Value("${minio.path}")
    private String PATH;

    public String saveImageFile(MultipartFile multipartFile)
            throws IOException, ServerException, InsufficientDataException,
            ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException,
            InvalidResponseException, XmlParserException, InternalException {

        boolean isExist = minioClient.bucketExists(
                BucketExistsArgs.builder()
                        .bucket(BUCKET_NAME)
                        .build());

        if (!isExist) {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(BUCKET_NAME)
                    .build());
        }

        String fileName = IMAGE_DIR + "/" + UUID.randomUUID() + "-" + multipartFile.getOriginalFilename();

        minioClient.putObject(PutObjectArgs.builder()
                .bucket(BUCKET_NAME)
                .object(fileName)
                .stream(multipartFile.getInputStream(), multipartFile.getSize(), -1)
                .contentType(multipartFile.getContentType())
                .build());

        return PATH + "/" + fileName;
    }

    public void deleteImage(String fileName)
            throws ServerException, InsufficientDataException, ErrorResponseException,
            IOException, NoSuchAlgorithmException, InvalidKeyException,
            InvalidResponseException, XmlParserException, InternalException {

        if(isHaveImage(fileName)){
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(BUCKET_NAME)
                            .object(fileName)
                            .build());
        }
    }

    public boolean isHaveImage(String fileName){
        try {
            minioClient.statObject(StatObjectArgs.builder().bucket(BUCKET_NAME).object(fileName).build());
            return true;
        } catch (Exception e) {
            return false;
        }
    }

}

PostMan으로 확인한 결과

  • 이미지 업로드

  • 이미지 삭제

0개의 댓글