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.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Build a High‑Performance MinIO Storage Service with Docker, Spring Boot & Vue

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 /data

Windows

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 /data

MINIO_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: false

Create 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
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

DockerSpring BootVuefile uploadMinioobject storage
Java High-Performance Architecture
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.