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.
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 :90904. 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 --reloadSet 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: wanghuiMinIO 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.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
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.
