Dosyalarınızı minIO ile yönetin | Spring Boot — minIO entegrasyonu
Projelerinizde dosyaları yönetmek için hangi teknolojileri kullanıyorsunuz ? MySQL, GridFS veya Firebase mi ? Birçok teknoloji kullanabiliriz aslında. Peki minIO ismini duydunuz mu daha önce ? Bu konuda araştırmalar yaparken karşılaştım minIO ile. Sonra projeme ekledim, denedim, hayran kaldım ve şimdi de anlatacağım.
MinIO Nedir ?
MinIO açık kaynak kodlu, yüksek performanslı bir nesne depolama sunucusudur. Amazon Web Services içerisindeki S3 (Simple Storage Service) aracıyla neredeyse aynı işlevi görmektedir. Bu aracı daha önce kullandınız mı bilmiyorum, kullanmadıysanız buyrun: AWS S3. Tabii ki localde kaldırdığımız hâli 😁 (Ayrıca çok güzel de bir arayüzü var..)
Spring Boot — MinIO Kullanımı
Haydi şimdi birkaç adımda minIO’yu projemize ekleyelim ve kullanalım.
Bağımlılık (Dependency)
pom.xml dosyamıza bağımlılığımızı aşağıdaki gibi ekleyelim.
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.4.5</version>
</dependency>
Maven reload yapmayı unutmayalım 😇
Konfigürasyon
application.properties dosyamızda gerekli konfigürasyon ayarlarını yapalım.
minio.bucket.name=miniobucket
minio.access.name=root
minio.access.secret=password
minio.url=http://127.0.0.1:9000
Buradaki kavramları açıklamak gerekirse;
minio.bucket.name : bucket ismimiz. (bucket’ı klasör olarak düşünebiliriz.)
minio.access.name : minIO konsol kullanıcı adımız.
minio.access.secret : minIO konsol parolamız.
minio.url : minIO’nun çalıştığı local url’imiz.
Bu arada belki işinize yarar, yukarıda yaptığımız konfigürasyon ayarlarına uyumlu docker compose’unu paylaşıyorum.
# docker-compose.yaml
version: "2"
services:
minio:
image: docker.io/bitnami/minio:2022
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: root
MINIO_ROOT_PASSWORD: password
MinIO Konfigürasyon Sınıfı
Servislerimizi yazmaya geçmeden önce servislerde kullanacağımız minioClient’ın konfigürasyon ayarlarını yaptığımız bir sınıf oluşturalım.
@Configuration
public class MinioConfig {
@Value("${minio.access.name}")
private String accessKey;
@Value("${minio.access.secret}")
private String secretKey;
@Value("${minio.url}")
private String minioUrl;
@Bean
@Primary
public MinioClient minioClient() {
return new MinioClient.Builder()
.credentials(accessKey, secretKey)
.endpoint(minioUrl)
.build();
}
}
Interface ve Service
MinIO süreçlerimizi yönetmemiz için bir servis katmanı oluşturacağız. Hadi önce service’de kullanacağımız methodlar için bir interface oluşturalım.
public interface MinioService {
List<Bucket> getAllBuckets() throws Exception;
// Tüm bucketları getirir.
String createBucket(String bucketName);
// Yeni bucket oluşturur.
byte[] getFile(String objectName,String bucketName);
// Dosyayı getirir.
MultipartFile uploadFile(MultipartFile file, String bucketName, String objectName) throws IOException;
// Dosya yükler.
void deleteFile(String objectName, String bucketName) throws IOException;
// Dosya siler.
}
Interface’imiz hazır olduğuna göre şimdi service’imizi yazabiliriz.
@Service
public class MinioServiceImpl implements MinioService {
@Autowired
MinioClient minioClient;
@Value("${minio.bucket.name}")
String defaultBucketName;
@Override
public List<Bucket> getAllBuckets() throws Exception {
return minioClient.listBuckets();
}
@Override
public String createBucket(String bucketName) {
try {
minioClient.makeBucket(MakeBucketArgs.builder()
.bucket(bucketName)
.build());
return bucketName + " bucked created.";
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
@Override
public byte[] getFile(String objectName, String bucketName) {
try {
InputStream file = minioClient.getObject(GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName).build());
return file.readAllBytes();
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
@Override
public MultipartFile uploadFile(MultipartFile file, String bucketName, String objectName) throws IOException {
try {
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.contentType(file.getContentType())
.stream(file.getInputStream(),file.getSize(),-1).build());
} catch (MinioException | InvalidKeyException | NoSuchAlgorithmException e) {
throw new IllegalStateException("The file cannot be upload on the internal storage. Please retry later", e);
}
return file;
}
@Override
public void deleteFile(String objectName, String bucketName) throws IOException {
try {
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
} catch (MinioException | InvalidKeyException | NoSuchAlgorithmException e) {
throw new IllegalStateException("The file cannot be delete on the internal storage. Please retry later", e);
}
}
}
Methodlar içerisinde kullandığımız kodları açıklama ihtiyacı duymadım çünkü incelendiğinde hepsinin gayet basit ve anlaşılabilir olduğunu düşünüyorum. Tanımladığımız minioClient ile tüm işlemleri kolayca belirledik.
Controller
Service sürecimizi tamamladık, şimdi de bir controller sınıfı oluşturalım ve endpointlerimizi yazalım.
@RestController
@RequestMapping("/api/file")
@RequiredArgsConstructor
public class MinIOController {
@Autowired
MinioService minioService;
@GetMapping(path = "/object/{objectName}")
// İstediğimiz bucket'tan istediğimiz image dosyasını dönderir.
public ResponseEntity<Resource> getBucketsImages(@PathVariable String objectName,@RequestParam String bucketName) {
var data = minioService.getFile(objectName,bucketName);
return ResponseEntity.ok()
.contentLength(data.length)
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\""+objectName+"\"")
.body(new ByteArrayResource(data));
}
@PostMapping(path = "/upload/{bucketName}", consumes = {"multipart/form-data"})
// İstediğimiz bucket'a gönderdiğimiz image dosyasını yüklememizi sağlar.
public ResponseEntity<Resource> savePostPhoto(@PathVariable("bucketName")String bucketName, @RequestPart("file") MultipartFile file) throws IOException {
var image = minioService.uploadFile(file,bucketName);
return ResponseEntity.ok().contentType(MediaType.valueOf(Objects.requireNonNull(image.getContentType())))
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"" + image.getOriginalFilename() + "\"")
.body(new ByteArrayResource(image.getBytes()));
}
@GetMapping(path = "/buckets")
// Tüm bucketları dönderir.
public List<Bucket> getAllBuckets() throws Exception {
return minioService.getAllBuckets();
}
@GetMapping(path = "/create-bucket/{bucketName}")
// Yeni bucket ekler.
public String createBucket(@PathVariable String bucketName){
return minioService.createBucket(bucketName);
}
}
Controller sınıfımızı da bu şekilde tamamlamış olduk. Sanırım projemiz artık kullanıma hazır hâle geldi.
Kullanım
Hemen postman kullanarak bir dosya yükleyelim. Ben “user” isimli bucket’ıma bir fotoğraf kaydetmek istiyorum.
Şimdi ise fotoğrafa localhost üzerinden ulaşmaya çalışalım. Fotoğrafı getiren endpoint’in “localhost:8080/api/file/object/dosya-ismi” olduğunu unutmayalım.
Gördüğünüz gibi fotoğrafımıza localhost:8080 adresinden ulaşabildik.
Son olarak minIO arayüzüne girerek bir de orda dosyamızın varlığını kontrol edelim. MinIO default olarak 9000 portunda çalışır, localhost:9000 adresine gidelim.
Gördüğünüz gibi user bucket’ımızın içine dosyamız başarıyla kaydedilmiş.
Sonuç
Böylelikle minIO teknolojisini projemize entegre ederek kullanmış olduk. Servislerimizi yazdık, endpointlerimizi belirledik ve denememizi yaptık. Sanırım söyleyeceğim de bir şey kalmadı diğer yazılarda görüşmek dileğiyle müsadenizi istiyorum. Teşekkürler. (Kaynakça kısmına yaptığımız işlemlerin olduğu bir GitHub repo adresini de ekliyorum.)