SpringBoot integrated Minio quick start tutorial

HBLOG
7 min readJan 30, 2024

--

一.minio introduce

MinIO is an object storage service based on the Apache License v2.0 open source agreement. It is compatible with the Amazon S3 cloud storage service interface and is very suitable for storing large-capacity unstructured data, such as pictures, videos, log files, backup data and container/virtual machine images, etc., and an object file can be of any size, ranging from several Kb to a maximum of 5T.

二.minio environment setup

This is built using docker-compose

# reference https://docs.min.io/docs/minio-docker-quickstart-guide.html
version: '3'
services:
minio:
image: minio/minio:latest # init image `minio/minio:latest`
container_name: minio #container name 'minio'
restart: unless-stopped #Specify the restart policy after the container exits to always restart, but do not consider containers that have been stopped when the Docker daemon is started.
volumes: # Data volume mounting path settings, mapping the local directory to the container directory
- "./minio/data:/data"
- "./minio/minio:/minio"
- "./minio/config:/root/.minio"
environment: # set env,Equivalent to -e in the docker run command
TZ: Asia/Shanghai
LANG: en_US.UTF-8
MINIO_PROMETHEUS_AUTH_TYPE: "public"
MINIO_ACCESS_KEY: "root" # login name
MINIO_SECRET_KEY: "password" # login password
command: server /data --console-address ":9001"
logging:
driver: "json-file"
options:
max-size: "100m"
ports: #mapping port
- "9000:9000" # file upload&preview port
- "9001:9001" # console access port

start service

docker-compose -f docker-compose-minio.yml -p minio up -d

access url:ip:9001/minio login name/password:root/password

三.springboot-minio code project

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springboot-demo</artifactId>
<groupId>com.et</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>minio</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.11</version>
</dependency>
</dependencies>
</project>

MinioConfig.java

package com.et.minio.config;
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
private String endpoint;
private String accessKey;
private String secretKey;
private String bucketName;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
}

OSSController.java

package com.et.minio.controller;

import com.et.minio.config.MinioConfig;
import com.et.minio.util.MinioUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;

@Slf4j
@RestController
@RequestMapping("/oss")
public class OSSController {

@Autowired
private MinioUtils minioUtils;

@Autowired
private MinioConfig minioConfig;

/**
* file upload
*
* @param file
*/
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file) {
try {
//file name
String fileName = file.getOriginalFilename();
String newFileName = System.currentTimeMillis() + "." + StringUtils.substringAfterLast(fileName, ".");
//type
String contentType = file.getContentType();
minioUtils.uploadFile(minioConfig.getBucketName(), file, newFileName, contentType);
return "upload success";
} catch (Exception e) {
e.printStackTrace();
log.error("upload fail");
return "upload fail";
}
}

/**
* delete
*
* @param fileName
*/
@DeleteMapping("/")
public void delete(@RequestParam("fileName") String fileName) {
minioUtils.removeFile(minioConfig.getBucketName(), fileName);
}

/**
* get file info
*
* @param fileName
* @return
*/
@GetMapping("/info")
public String getFileStatusInfo(@RequestParam("fileName") String fileName) {
return minioUtils.getFileStatusInfo(minioConfig.getBucketName(), fileName);
}

/**
* get file url
*
* @param fileName
* @return
*/
@GetMapping("/url")
public String getPresignedObjectUrl(@RequestParam("fileName") String fileName) {
return minioUtils.getPresignedObjectUrl(minioConfig.getBucketName(), fileName);
}

/**
* file download
*
* @param fileName
* @param response
*/
@GetMapping("/download")
public void download(@RequestParam("fileName") String fileName, HttpServletResponse response) {
try {
InputStream fileInputStream = minioUtils.getObject(minioConfig.getBucketName(), fileName);
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
response.setContentType("application/force-download");
response.setCharacterEncoding("UTF-8");
IOUtils.copy(fileInputStream, response.getOutputStream());
} catch (Exception e) {
log.error("download fail");
}
}

}

MinioUtil.java

package com.et.minio.util;

import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;

/**
* MinIO Utils
*
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class MinioUtils {

private final MinioClient minioClient;

/****************************** Operate Bucket Start ******************************/

/**
* init Bucket when start SpringBoot container
* create bucket if the bucket is not exists
*
* @param bucketName
*/
@SneakyThrows(Exception.class)
private void createBucket(String bucketName) {
if (!bucketExists(bucketName)) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
}

/**
* verify Bucket is exist?,true:false
*
* @param bucketName
* @return
*/
@SneakyThrows(Exception.class)
public boolean bucketExists(String bucketName) {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
}

/**
* get Bucket strategy
*
* @param bucketName
* @return
*/
@SneakyThrows(Exception.class)
public String getBucketPolicy(String bucketName) {
return minioClient.getBucketPolicy(GetBucketPolicyArgs
.builder()
.bucket(bucketName)
.build());
}

/**
* get all Bucket list
*
* @return
*/
@SneakyThrows(Exception.class)
public List<Bucket> getAllBuckets() {
return minioClient.listBuckets();
}

/**
* Get related information based on bucketName
*
* @param bucketName
* @return
*/
@SneakyThrows(Exception.class)
public Optional<Bucket> getBucket(String bucketName) {
return getAllBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
}

/**
* Delete Bucket based on bucketName, true: deletion successful; false: deletion failed, file may no longer exist
*
* @param bucketName
* @throws Exception
*/
@SneakyThrows(Exception.class)
public void removeBucket(String bucketName) {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
}

/****************************** Operate Bucket End ******************************/


/****************************** Operate Files Start ******************************/

/**
* check file is exist
*
* @param bucketName
* @param objectName
* @return
*/
public boolean isObjectExist(String bucketName, String objectName) {
boolean exist = true;
try {
minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
} catch (Exception e) {
log.error("[MinioUtils]>>>>check file exist, Exception:", e);
exist = false;
}
return exist;
}

/**
* check directory exist?
*
* @param bucketName
* @param objectName
* @return
*/
public boolean isFolderExist(String bucketName, String objectName) {
boolean exist = false;
try {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(false).build());
for (Result<Item> result : results) {
Item item = result.get();
if (item.isDir() && objectName.equals(item.objectName())) {
exist = true;
}
}
} catch (Exception e) {
log.error("[MinioUtils]>>>>check file exist, Exception:", e);
exist = false;
}
return exist;
}

/**
* Query files based on file prefix
*
* @param bucketName
* @param prefix
* @param recursive
* @return MinioItem
*/
@SneakyThrows(Exception.class)
public List<Item> getAllObjectsByPrefix(String bucketName,
String prefix,
boolean recursive) {
List<Item> list = new ArrayList<>();
Iterable<Result<Item>> objectsIterator = minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());
if (objectsIterator != null) {
for (Result<Item> o : objectsIterator) {
Item item = o.get();
list.add(item);
}
}
return list;
}

/**
* get file InputStream
*
* @param bucketName
* @param objectName
* @return
*/
@SneakyThrows(Exception.class)
public InputStream getObject(String bucketName, String objectName) {
return minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
}

/**
* Breakpoint download
*
* @param bucketName
* @param objectName
* @param offset
* @param length
* @return
*/
@SneakyThrows(Exception.class)
public InputStream getObject(String bucketName, String objectName, long offset, long length) {
return minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.offset(offset)
.length(length)
.build());
}

/**
* Get the list of files under the path
*
* @param bucketName
* @param prefix
* @param recursive
* @return
*/
public Iterable<Result<Item>> listObjects(String bucketName, String prefix, boolean recursive) {
return minioClient.listObjects(
ListObjectsArgs.builder()
.bucket(bucketName)
.prefix(prefix)
.recursive(recursive)
.build());
}

/**
* use MultipartFile to upload files
*
* @param bucketName
* @param file
* @param objectName
* @param contentType
* @return
*/
@SneakyThrows(Exception.class)
public ObjectWriteResponse uploadFile(String bucketName, MultipartFile file, String objectName, String contentType) {
InputStream inputStream = file.getInputStream();
return minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.contentType(contentType)
.stream(inputStream, inputStream.available(), -1)
.build());
}

/**
* picture upload
* @param bucketName
* @param imageBase64
* @param imageName
* @return
*/
public ObjectWriteResponse uploadImage(String bucketName, String imageBase64, String imageName) {
if (!StringUtils.isEmpty(imageBase64)) {
InputStream in = base64ToInputStream(imageBase64);
String newName = System.currentTimeMillis() + "_" + imageName + ".jpg";
String year = String.valueOf(new Date().getYear());
String month = String.valueOf(new Date().getMonth());
return uploadFile(bucketName, year + "/" + month + "/" + newName, in);

}
return null;
}

public static InputStream base64ToInputStream(String base64) {
ByteArrayInputStream stream = null;
try {
byte[] bytes = new BASE64Decoder().decodeBuffer(base64.trim());
stream = new ByteArrayInputStream(bytes);
} catch (Exception e) {
e.printStackTrace();
}
return stream;
}


/**
* upload local files
*
* @param bucketName
* @param objectName
* @param fileName
* @return
*/
@SneakyThrows(Exception.class)
public ObjectWriteResponse uploadFile(String bucketName, String objectName, String fileName) {
return minioClient.uploadObject(
UploadObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.filename(fileName)
.build());
}

/**
* upload files based on stream
*
* @param bucketName
* @param objectName
* @param inputStream
* @return
*/
@SneakyThrows(Exception.class)
public ObjectWriteResponse uploadFile(String bucketName, String objectName, InputStream inputStream) {
return minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(inputStream, inputStream.available(), -1)
.build());
}

/**
* create file or direatory
*
* @param bucketName
* @param objectName
* @return
*/
@SneakyThrows(Exception.class)
public ObjectWriteResponse createDir(String bucketName, String objectName) {
return minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.stream(new ByteArrayInputStream(new byte[]{}), 0, -1)
.build());
}

/**
* get file info
*
* @param bucketName
* @param objectName
* @return
*/
@SneakyThrows(Exception.class)
public String getFileStatusInfo(String bucketName, String objectName) {
return minioClient.statObject(
StatObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build()).toString();
}

/**
* copy file
*
* @param bucketName
* @param objectName
* @param srcBucketName
* @param srcObjectName
*/
@SneakyThrows(Exception.class)
public ObjectWriteResponse copyFile(String bucketName, String objectName, String srcBucketName, String srcObjectName) {
return minioClient.copyObject(
CopyObjectArgs.builder()
.source(CopySource.builder().bucket(bucketName).object(objectName).build())
.bucket(srcBucketName)
.object(srcObjectName)
.build());
}

/**
* delete file
*
* @param bucketName
* @param objectName
*/
@SneakyThrows(Exception.class)
public void removeFile(String bucketName, String objectName) {
minioClient.removeObject(
RemoveObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
}

/**
* batch delete file
*
* @param bucketName
* @param keys
* @return
*/
public void removeFiles(String bucketName, List<String> keys) {
List<DeleteObject> objects = new LinkedList<>();
keys.forEach(s -> {
objects.add(new DeleteObject(s));
try {
removeFile(bucketName, s);
} catch (Exception e) {
log.error("[MinioUtil]>>>>batch delete file,Exception:", e);
}
});
}

/**
* get file url
*
* @param bucketName
* @param objectName
* @param expires
* @return url
*/
@SneakyThrows(Exception.class)
public String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) {
GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder().expiry(expires).bucket(bucketName).object(objectName).build();
return minioClient.getPresignedObjectUrl(args);
}

/**
* get file url
*
* @param bucketName
* @param objectName
* @return url
*/
@SneakyThrows(Exception.class)
public String getPresignedObjectUrl(String bucketName, String objectName) {
GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
.bucket(bucketName)
.object(objectName)
.method(Method.GET).build();
return minioClient.getPresignedObjectUrl(args);
}

/**
* change URLDecoder to UTF8
*
* @param str
* @return
* @throws UnsupportedEncodingException
*/
public String getUtf8ByURLDecoder(String str) throws UnsupportedEncodingException {
String url = str.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
return URLDecoder.decode(url, "UTF-8");
}
}

code repository

四.Check

we use postman to test our upload interface,such as:

test success,we can login minio‘s console,Check out the file that we just uploaded

五.reference

--

--