Build a High‑Performance MinIO Storage Service with Docker, Spring Boot & Vue
This tutorial walks through installing MinIO with Docker on Linux/macOS and Windows, configuring a Spring Boot backend with MinIO Java client, creating a reusable component for file upload, and implementing three Vue/Element‑UI front‑end upload methods, complete with code snippets and screenshots.
What is MinIO
MinIO is a high‑performance object storage server released under the GNU AGPL v3.0, compatible with Amazon S3 and suitable for machine‑learning, analytics, and application data workloads.
1. Use Docker to set up MinIO service
Linux/macOS
docker run -p 9000:9000 \
--name minio1 \
-v /mnt/data:/data \
-e "MINIO_ROOT_USER=AKIAIOSFODNN7EXAMPLE" \
-e "MINIO_ROOT_PASSWORD=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
minio/minio server /dataWindows
docker run -p 9000:9000 \
--name minio1 \
-v D:\data:/data \
-e "MINIO_ROOT_USER=AKIAIOSFODNN7EXAMPLE" \
-e "MINIO_ROOT_PASSWORD=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \
minio/minio server /dataMINIO_ROOT_USER: user key
MINIO_ROOT_PASSWORD: user secret
The commands start a single‑node MinIO instance; refer to the official documentation for distributed deployment.
After starting, access the graphical UI at http://localhost:9000.
2. Build Spring Boot environment
Add the following Maven dependencies to your Spring Boot project:
<!-- thymeleaf template engine -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- MinIO Java client -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.1</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>Configure MinIO properties in application.yml:
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
minio:
access-key: AKIAIOSFODNN7EXAMPLE
secret-key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
url: http://localhost:9000
bucket-name: wdhcr
thymeleaf:
cache: falseCreate a configuration class to bind these properties and expose a MinioClient bean:
@Configuration
@ConfigurationProperties(prefix = "spring.minio")
@Data
public class MinioConfiguration {
private String accessKey;
private String secretKey;
private String url;
private String bucketName;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(url)
.credentials(accessKey, secretKey)
.build();
}
}Implement a reusable component that encapsulates common MinIO operations:
@Component
public class MinioComp {
@Autowired
private MinioClient minioClient;
@Autowired
private MinioConfiguration configuration;
/** Get a signed policy for temporary upload */
public Map getPolicy(String fileName, ZonedDateTime time) {
PostPolicy postPolicy = new PostPolicy(configuration.getBucketName(), time);
postPolicy.addEqualsCondition("key", fileName);
try {
Map<String, String> map = minioClient.getPresignedPostFormData(postPolicy);
HashMap<String, String> map1 = new HashMap<>();
map.forEach((k, v) -> map1.put(k.replaceAll("-", ""), v));
map1.put("host", configuration.getUrl() + "/" + configuration.getBucketName());
return map1;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/** Get a presigned URL for downloading a file */
public String getUrl(String objectName, int time, TimeUnit timeUnit) {
try {
return minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(configuration.getBucketName())
.object(objectName)
.expiry(time, timeUnit)
.build());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/** Upload a file */
public void upload(MultipartFile file, String fileName) {
try {
InputStream inputStream = file.getInputStream();
minioClient.putObject(PutObjectArgs.builder()
.bucket(configuration.getBucketName())
.object(fileName)
.stream(inputStream, file.getSize(), -1)
.contentType(file.getContentType())
.build());
} catch (Exception e) {
e.printStackTrace();
}
}
}The component provides methods for:
Receiving a MultipartFile from the front end and uploading it to MinIO.
Generating a signed formData for direct browser upload.
Creating a temporary URL for front‑end upload.
Obtaining a presigned download URL (valid up to 7 days).
Front‑end page (Vue + Element‑UI)
The page demonstrates three upload approaches:
Traditional upload using the backend /upload endpoint.
Direct form‑data upload with a signed policy.
Direct URL upload using a presigned PUT URL.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
<title>Upload Image</title>
</head>
<body>
<div id="app">
<el-row :gutter="2">
<!-- Traditional upload -->
<el-col :span="8">
<center><h3>Traditional Upload</h3></center>
<el-upload class="upload-demo" action="#" drag :http-request="uploadHandle">
<i class="el-icon-upload"></i>
<div class="el-upload__text">Drag files here, or <em>click to upload</em></div>
<div class="el-upload__tip" slot="tip">Only jpg/png, max 500KB</div>
</el-upload>
<div v-if="imgUrl"><img :src="imgUrl" style="width:40px;height:40px"></img></div>
</el-col>
<!-- Form‑data direct upload -->
<el-col :span="8">
<center><h3>Form‑Data Direct Upload</h3></center>
<el-upload class="upload-demo" action="#" drag :http-request="httpRequestHandle">
<i class="el-icon-upload"></i>
<div class="el-upload__text">Drag files here, or <em>click to upload</em></div>
<div class="el-upload__tip" slot="tip">Only jpg/png, max 500KB</div>
</el-upload>
<div v-if="directUrl"><img :src="directUrl" style="width:40px;height:40px"></img></div>
</el-col>
<!-- URL direct upload -->
<el-col :span="8">
<center><h3>URL Direct Upload</h3></center>
<el-upload class="upload-demo" action="#" drag :http-request="UrlUploadHandle">
<i class="el-icon-upload"></i>
<div class="el-upload__text">Drag files here, or <em>click to upload</em></div>
<div class="el-upload__tip" slot="tip">Only jpg/png, max 500KB</div>
</el-upload>
<div v-if="uploadUrl"><img :src="uploadUrl" style="width:40px;height:40px"></img></div>
</el-col>
</el-row>
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
new Vue({
el: '#app',
data: function() { return { imgUrl: '', directUrl: '', uploadUrl: '' }; },
methods: {
uploadHandle(options) { this.traditionPost(options.file); },
traditionPost(file) {
const form = new FormData();
form.append('fileName', file.name);
form.append('file', file);
this.axiosPost('post', '/upload', form).then(res => {
if (res.status === 200) this.imgUrl = res.data.data; else alert('Upload failed!');
});
},
getpolicy(file) {
axios.get('policy?fileName=' + file.name).then(response => {
const { xamzalgorithm, xamzcredential, policy, xamzsignature, xamzdate, host } = response.data.data;
const formData = new FormData();
formData.append('key', file.name);
formData.append('x-amz-algorithm', xamzalgorithm);
formData.append('x-amz-credential', xamzcredential);
formData.append('policy', policy);
formData.append('x-amz-signature', xamzsignature);
formData.append('x-amz-date', xamzdate);
formData.append('file', file);
this.axiosPost('post', host, formData).then(res => {
if (res.status === 204) {
axios.get('url?fileName=' + file.name).then(r => { this.directUrl = r.data.data; });
} else alert('Upload failed!');
});
});
},
httpRequestHandle(options) { this.getpolicy(options.file); },
UrlUploadHandle(options) { this.getUploadUrl(options.file); },
getUploadUrl(file) {
axios.get('uploadUrl?fileName=' + file.name).then(response => {
const url = response.data.data;
const config = { 'Content-Type': file.type };
this.axiosPost('put', url, file, config).then(res => {
if (res.status === 200) {
axios.get('url?fileName=' + file.name).then(r => { this.uploadUrl = r.data.data; });
} else alert('Upload failed!');
});
});
},
axiosPost(method, url, data, config) {
return axios({ method, url, data, headers: config })
.then(resp => resp)
.catch(error => "exception=" + error);
}
}
});
</script>
</body>
</html>Page effect
You can try each implementation to see the different upload behaviours.
The complete steps above demonstrate how to build a high‑performance MinIO‑based storage service using Docker, Spring Boot, and Vue.
Project repository: https://gitee.com/jack_whh/minio-upload
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Java High-Performance Architecture
Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
