Cloud Native 26 min read

Deploy MinIO on CentOS 7 and Integrate with Spring Boot for Fast Chunked File Uploads

This guide walks through installing MinIO on CentOS 7, configuring it with Docker, setting up a Spring Boot project, and using Java code to perform chunked, resumable, and instant file uploads to MinIO's object storage, complete with sample code and testing steps.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Deploy MinIO on CentOS 7 and Integrate with Spring Boot for Fast Chunked File Uploads

Overview

MinIO is an open‑source object storage server that runs on Linux, Windows, and macOS, offering a simple, scalable, and highly available solution supporting object, block, and file storage.

Easy to install and configure via a binary file with a Web UI.

Scalable across multiple nodes for high availability and fault tolerance.

Provides data redundancy, replication, and failover for high reliability.

Supports SSL/TLS encryption, access control, and data encryption for security.

Offers multi‑language SDKs (Java, Python, Ruby, Go) and integrates with cloud‑native platforms.

Application Scenarios

MinIO can be used for large‑scale data storage, media storage, cloud‑native applications, data protection and disaster recovery, as well as big‑data and AI workloads that require fast, parallel data access.

MinIO Chunked Upload Steps

Using Spring Boot and MinIO to implement chunked upload, instant upload, and resumable upload involves:

Front‑end selects a file and splits it into chunks, generating a unique identifier for each.

Each chunk is uploaded to MinIO via its Java SDK, using a key that includes the base name and chunk ID.

After all chunks are uploaded, a back‑end API merges them into the final file, either on the application server or using MinIO’s built‑in merge feature.

Instant upload checks the file’s MD5; if the file already exists, the server returns the URL directly.

Resumable upload records uploaded chunk indices and resumes from the next missing chunk after a failure.

Error handling ensures the process continues smoothly despite network or service interruptions.

Install MinIO on CentOS 7

1. Create a target directory: mkdir minio 2. Pull the MinIO Docker image: docker pull minio/minio 3. Run the container with required environment variables and ports:

docker run -p 9000:9000 -p 9090:9090 \
    --net=host \
    --name minio \
    -d --restart=always \
    -e "MINIO_ACCESS_KEY=YourAccessKey" \
    -e "MINIO_SECRET_KEY=YourSecretKey" \
    minio/minio server /data --console-address :9000 --address :9090

4. Ensure the firewall allows ports 9000 and 9090:

firewall-cmd --zone=public --add-port=9000/tcp --permanent
firewall-cmd --zone=public --add-port=9090/tcp --permanent
firewall-cmd --reload

Set Up Spring Boot Environment

Add MinIO and related dependencies to pom.xml:

<!-- MinIO Java client -->
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.5.2</version>
</dependency>

<!-- JWT and logging dependencies omitted for brevity -->

Configure application properties (e.g., application.yml) with MinIO connection details:

minio:
  access-key: dAMaxkWaXUD1CV1JHbqw
  secret-key: AXt3SD0JFkDENFbMeJKOOQb5wj8KvabZWu33Rs84
  url: http://192.168.18.14:9090
  bucket-name: wanghui

MinIO Configuration Class

package com.xiaohui.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 = "spring.minio")
public class MinioConfig {
    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();
    }
}

MinIO Utility Class

package com.xiaohui.utils;

import com.xiaohui.config.MinioConfig;
import io.minio.*;
import io.minio.http.Method;
import lombok.SneakyThrows;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URLEncoder;
import java.time.ZonedDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class MinioUtils {
    @Autowired
    private MinioClient minioClient;
    @Autowired
    private MinioConfig configuration;

    public boolean existBucket(String name) {
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
                return true;
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @SneakyThrows
    public Map getPolicy(String fileName, ZonedDateTime time) {
        PostPolicy postPolicy = new PostPolicy(configuration.getBucketName(), time);
        postPolicy.addEqualsCondition("key", fileName);
        Map<String, String> map = minioClient.getPresignedPostFormData(postPolicy);
        HashMap<String, String> result = new HashMap<>();
        map.forEach((k, v) -> result.put(k.replaceAll("-", ""), v));
        result.put("host", configuration.getUrl() + "/" + configuration.getBucketName());
        return result;
    }

    public String getPolicyUrl(String objectName, Method method, int time, TimeUnit timeUnit) {
        try {
            return minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(method)
                            .bucket(configuration.getBucketName())
                            .object(objectName)
                            .expiry(time, timeUnit)
                            .build());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    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();
        }
    }

    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;
        }
    }

    public ResponseEntity<byte[]> download(String fileName) {
        try (InputStream in = minioClient.getObject(GetObjectArgs.builder()
                .bucket(configuration.getBucketName())
                .object(fileName)
                .build());
             ByteArrayOutputStream out = new ByteArrayOutputStream()) {
            IOUtils.copy(in, out);
            byte[] bytes = out.toByteArray();
            HttpHeaders headers = new HttpHeaders();
            headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            headers.setContentLength(bytes.length);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            return new ResponseEntity<>(bytes, headers, HttpStatus.SUCCESS);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public String getFileUrl(String objectFile) {
        try {
            return minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs.builder()
                            .method(Method.GET)
                            .bucket(configuration.getBucketName())
                            .object(objectFile)
                            .build());
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

Ajax Result Class

package com.xiaohui.utils;

import java.util.HashMap;

public class AjaxResult extends HashMap<String, Object> {
    private static final long serialVersionUID = 1L;
    public static final String CODE_TAG = "code";
    public static final String MSG_TAG = "msg";
    public static final String DATA_TAG = "data";

    public AjaxResult() {}
    public AjaxResult(int code, String msg) {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
    }
    public AjaxResult(int code, String msg, Object data) {
        super.put(CODE_TAG, code);
        super.put(MSG_TAG, msg);
        if (data != null) {
            super.put(DATA_TAG, data);
        }
    }
    public static AjaxResult success() {
        return success("Operation successful");
    }
    public static AjaxResult success(Object data) {
        return success("Operation successful", data);
    }
    public static AjaxResult success(String msg) {
        return success(msg, null);
    }
    public static AjaxResult success(String msg, Object data) {
        return new AjaxResult(HttpStatus.SUCCESS, msg, data);
    }
    public static AjaxResult warn(String msg) {
        return warn(msg, null);
    }
    public static AjaxResult warn(String msg, Object data) {
        return new AjaxResult(HttpStatus.WARN, msg, data);
    }
    public static AjaxResult error() {
        return error("Operation failed");
    }
    public static AjaxResult error(String msg) {
        return error(msg, null);
    }
    public static AjaxResult error(String msg, Object data) {
        return new AjaxResult(HttpStatus.ERROR, msg, data);
    }
    public static AjaxResult error(int code, String msg) {
        return new AjaxResult(code, msg, null);
    }
    @Override
    public AjaxResult put(String key, Object value) {
        super.put(key, value);
        return this;
    }
}

MinIO File Controller

package com.xiaohui.controller;

import com.xiaohui.utils.AjaxResult;
import com.xiaohui.utils.MinioUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.HashMap;

@CrossOrigin
@RestController
@RequestMapping("/api")
public class MinioFileUploadController {
    @Autowired
    private MinioUtils minioUtils;

    @GetMapping("/upload")
    public AjaxResult uploadFile(@RequestParam("file") MultipartFile file, String fileName) {
        minioUtils.upload(file, fileName);
        return AjaxResult.success("Upload successful");
    }

    @GetMapping("/download")
    public ResponseEntity downloadFile(@RequestParam("fileName") String fileName) {
        return minioUtils.download(fileName);
    }

    @GetMapping("/getUrl")
    public AjaxResult getFileUrl(@RequestParam("fileName") String fileName) {
        HashMap<String, String> map = new HashMap<>();
        map.put("FileUrl", minioUtils.getFileUrl(fileName));
        return AjaxResult.success(map);
    }
}

Function Testing

Upload large files via the provided API, verify the generated URLs, and download the files to confirm integrity. Screenshots show Docker container status, MinIO console access, and successful file retrieval.

DockerMinIOchunked upload
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

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.