Spring boot: uploading and downloading file from Minio object store
If you have landed on this page means either your working with spring boot and trying to upload an file to a privately hosted minio object store server. so we know spring boot has gained popularity for its simple approach to bootstrap an application in very less time, Also minio is gaining lot of popularity these days as people want to host their own data in private cloud space. if your wondering what is minio its yet another object store making the owner and managed by your system administrators. And by the way if you need getting started guide with minio please follow the below guide.
So now we shall see how to upload and download file from minio server using spring boot.
1) Dependency
only dependency we need is minio client sdk. as developer most of us tend to copy paste without reading completely so i am pasting complete dependency of my set up.
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ashrithgn</groupId>
<artifactId>miniodemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>miniodemo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>6.0.8</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.8.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.8.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-io -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2) Now Lets add required constants into our application.properties file.
minio.buckek.name=test
minio.default.folder=/
server.port=8000
minio.access.name=minioadmin
minio.access.secret=minioadmin
minio.url=http://127.0.0.1:9000
no we have partially completed our set up. so just like s3 bucket is place where the files are stored in simple words its the root folder or mount point, and access and secret is the authentication required to access minio server.
3) Creating Configuration class which return the minio client.
As a Spring boot developer i have bad notion of autowiring stuff as it creates singleton object and does not create new object every time. below code is grabs config form property file and returns an instance of the minio client which we use to upload and download file using the client.
//add your package
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.access.name}")
String accessKey;
@Value("${minio.access.secret}")
String accessSecret;
@Value("${minio.url}")
String minioUrl;
@Bean
public MinioClient generateMinioClient() {
try {
MinioClient client = new MinioClient(minioUrl, accessKey, accessSecret);
return client;
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
}
The Adaptor Service class which implements file upload and download logic.
As i am not a design expert so naming convection used for this could be wrong, so make sure you rename according to your design spec :-P.
// package is mission
import io.minio.MinioClient;
import io.minio.messages.Bucket;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.io.*;
import java.util.List;
@Service
public class MinioAdapter {
@Autowired
MinioClient minioClient;
@Value("${minio.buckek.name}")
String defaultBucketName;
@Value("${minio.default.folder}")
String defaultBaseFolder;
public List<Bucket> getAllBuckets() {
try {
return minioClient.listBuckets();
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
public void uploadFile(String name, byte[] content) {
File file = new File("/tmp/" + name);
file.canWrite();
file.canRead();
try {
FileOutputStream iofs = new FileOutputStream(file);
iofs.write(content);
minioClient.putObject(defaultBucketName, defaultBaseFolder + name, file.getAbsolutePath());
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
public byte[] getFile(String key) {
try {
InputStream obj = minioClient.getObject(defaultBucketName, defaultBaseFolder + "/" + key);
byte[] content = IOUtils.toByteArray(obj);
obj.close();
return content;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@PostConstruct
public void init() {
}
}
So Now lets implement controller, basically i have written three end points which list the available buckets, and one end point to upload the file, and another end point to download the file.
import com.costrategix.s3demo.config.s3.MinioAdapter;
import io.minio.messages.Bucket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
public class MinioStorageController {
@Autowired
MinioAdapter minioAdapter;
@GetMapping(path = "/buckets")
public List<Bucket> listBuckets() {
return minioAdapter.getAllBuckets();
}
@PostMapping(path = "/upload", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public Map<String, String> uploadFile(@RequestPart(value = "file", required = false) MultipartFile files) throws IOException {
minioAdapter.uploadFile(files.getOriginalFilename(), files.getBytes());
Map<String, String> result = new HashMap<>();
result.put("key", files.getOriginalFilename());
return result;
}
@GetMapping(path = "/download")
public ResponseEntity<ByteArrayResource> uploadFile(@RequestParam(value = "file") String file) throws IOException {
byte[] data = minioAdapter.getFile(file);
ByteArrayResource resource = new ByteArrayResource(data);
return ResponseEntity
.ok()
.contentLength(data.length)
.header("Content-type", "application/octet-stream")
.header("Content-disposition", "attachment; filename=\"" + file + "\"")
.body(resource);
}
}
So you can use Postman or any API testing tool and hit the end point and verify if the files are being uploaded as well as it could be downloaded and opened. you also refer to similar tutorials below