> 技术文档 > 2025 docker 安装 minio 操作实践、java实践_minio社区版功能限制

2025 docker 安装 minio 操作实践、java实践_minio社区版功能限制


2025 docker 安装 MinIO 操作实践

最近使用docker 安装MinIO时,发现界面不一样了,最新版的 MinIO 社区版中缺少用户设置,这是因为 MinIO 近期对其社区版的用户界面 (UI) 功能进行了重大调整。过去,MinIO 的 Web 控制台提供了账户和策略管理、配置设置等功能。然而,在最新的社区版中,这些功能已被移除,现在其 Web 界面主要作为一个对象浏览器。

主要调整:
UI 功能的限制: 社区版的 Web UI 不再支持用户和策略管理、桶管理、配置管理等。
转向命令行: 你需要使用 MinIO Client (mc) 工具来执行这些管理操作,例如添加用户、设置权限等。
商业版区分: MinIO 的商业版本(AIStor)提供了更完整的 Web UI 管理功能以及其他企业级特性和支持。

具体实践流程( 部署单节点 MinIO)

准备工作
1、拉取MinIO镜像

docker pull minio

2、在你的宿主机上创建一个目录,用于存储 MinIO 的数据

mkdir -p ~/home/minio/data

3、启动 MinIO 容器

docker run \\ -p 9000:9000 \\ -p 9090:9090 \\ --name minio \\ -v ~/home/minio/data:/data \\ -e MINIO_ROOT_USER=minioadmin \\ -e MINIO_ROOT_PASSWORD=minioadmin \\ minio/minio server /data --console-address \":9090\"

4、验证 MinIO 运行
打开浏览器,访问 http://localhost:9090。你将看到 MinIO 的登录界面。输入你在启动命令中设置的用户名 (minioadmin) 和密码 (minioadmin) 即可登录 MinIO Console。

效果如下:
2025 docker 安装 minio 操作实践、java实践_minio社区版功能限制

使用 MinIO Client (mc) 管理 MinIO
1、Linux 系统下载mc

wget https://dl.min.io/client/mc/release/linux-amd64/mc

下载完成后,你需要给下载下来的 mc 文件添加执行权限

chmod +x mc

将 mc 移动到 PATH 路径中 (可选但推荐)

sudo mv mc /usr/local/bin/

验证安装

mc --version

2、将 MinIO 服务器添加到 mc 的配置中

mc alias set myminio http://localhost:9000 minioadmin minioadmin

(这里的myminio 是你http://localhost:9000的别名,后续在执行像 mc admin user add myminio newuser newpassword 这样的操作时,myminio 就会自动被解析成您之前配置的那个完整的 MinIO 服务器地址。这大大简化了命令行操作。)

3、通过命令行来操作bucket

创建一个桶 (Bucket)

mc mb myminio/mybucket

上传文件到桶

mc cp /path/to/your/local/file.txt myminio/mybucket/

列出桶中的文件

mc ls myminio/mybucket

注意:关于bucket的操作直接在Web UI 中进行

4、用户策略管理(重要,在 Web UI 中已不可用

创建新用户

mc admin user add myminio newuser newpassword

创建策略:假设你想创建一个只读策略,允许访问 mybucket,需要创建一个名为 readonly-policy.json 的文件

{ \"Version\": \"2012-10-17\", \"Statement\": [ { \"Effect\": \"Allow\", \"Action\": [ \"s3:GetObject\", \"s3:ListBucket\" ], \"Resource\": [ \"arn:aws:s3:::mybucket\", \"arn:aws:s3:::mybucket/*\" ] } ]}

上传并应用策略:

mc admin policy add myminio myreadonlypolicy readonly-policy.json

为用户附加策略:

mc admin policy set myminio myreadonlypolicy user=newuser

java中使用MinIO

1、安装对应依赖

  cn.hutool hutool-all 5.8.37  io.minio minio 8.5.17

2、配置yaml文件

minio: url: http://localhost:9000 accessKey: minioadmin secretKey: minioadmin

3、创建对应工具类

package com.szq.detection_system_back.utils;import cn.hutool.core.io.FileUtil;import io.minio.*;import io.minio.messages.Item;import io.minio.http.Method; // 导入 Method 类import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Component;import java.io.File;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.nio.file.Path;import java.nio.file.Paths;import java.util.ArrayList;import java.util.List;import java.util.concurrent.TimeUnit; // 导入 TimeUnitimport java.util.logging.Logger;/** * MinIO 工具类,用于文件上传、下载、删除、列表展示和获取文件URL。 */@Componentpublic class MinioUtil { private static final Logger logger = Logger.getLogger(MinioUtil.class.getName()); @Value(\"${minio.url}\") private String minioUrl; @Value(\"${minio.accessKey}\") private String accessKey; @Value(\"${minio.secretKey}\") private String secretKey; private MinioClient minioClient; /** * 初始化 MinioClient */ private void initMinioClient() { if (minioClient == null) { minioClient = MinioClient.builder()  .endpoint(minioUrl)  .credentials(accessKey, secretKey)  .build(); } } /** * 检查桶是否存在,如果不存在则创建 * * @param bucketName 桶名称 * @throws Exception 如果操作失败 */ public void createBucketIfNotExists(String bucketName) throws Exception { initMinioClient(); boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); if (!found) { minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); logger.info(\"桶 \'\" + bucketName + \"\' 创建成功.\"); } else { logger.info(\"桶 \'\" + bucketName + \"\' 已存在.\"); } } /** * 上传单个文件 * * @param bucketName 桶名称 * @param objectName 对象名称(MinIO中存储的文件名) * @param localFilePath 本地文件路径 * @throws Exception 如果操作失败 */ public void uploadFile(String bucketName, String objectName, String localFilePath) throws Exception { initMinioClient(); createBucketIfNotExists(bucketName); // 确保桶存在 try (FileInputStream fis = new FileInputStream(localFilePath)) { minioClient.putObject(  PutObjectArgs.builder() .bucket(bucketName) .object(objectName) .stream(fis, fis.available(), -1) .build()); logger.info(\"文件 \'\" + objectName + \"\' 成功上传到桶 \'\" + bucketName + \"\'.\"); } } /** * 下载文件 * * @param bucketName 桶名称 * @param objectName 对象名称(MinIO中存储的文件名) * @param downloadDirectory 下载目录 * @param desiredLocalFileName 下载到本地的文件名(包含扩展名) * @return 下载后的完整文件路径 * @throws Exception 如果操作失败 */ public String downloadFile(String bucketName, String objectName, String downloadDirectory, String desiredLocalFileName) throws Exception { initMinioClient(); FileUtil.mkdir(downloadDirectory); // 确保下载目录存在 String downloadFilePath = Paths.get(downloadDirectory, desiredLocalFileName).toString(); try (InputStream stream = minioClient.getObject( GetObjectArgs.builder() .bucket(bucketName) .object(objectName) .build()); FileOutputStream fos = new FileOutputStream(downloadFilePath)) { byte[] buffer = new byte[1024]; int bytesRead; while ((bytesRead = stream.read(buffer)) != -1) { fos.write(buffer, 0, bytesRead); } logger.info(\"文件 \'\" + objectName + \"\' 成功下载到: \" + downloadFilePath); return downloadFilePath; } } /** * 列出桶中的所有对象 * * @param bucketName 桶名称 * @return 对象名称列表 * @throws Exception 如果操作失败 */ public List<String> listObjects(String bucketName) throws Exception { initMinioClient(); List<String> objectNames = new ArrayList<>(); Iterable<Result<Item>> results = minioClient.listObjects( ListObjectsArgs.builder().bucket(bucketName).build()); logger.info(\"列出桶 \'\" + bucketName + \"\' 中的对象:\"); for (Result<Item> result : results) { Item item = result.get(); objectNames.add(item.objectName()); logger.info(\" - \" + item.objectName() + \" (大小: \" + item.size() + \" bytes)\"); } return objectNames; } /** * 删除对象 * * @param bucketName 桶名称 * @param objectName 对象名称 * @throws Exception 如果操作失败 */ public void removeObject(String bucketName, String objectName) throws Exception { initMinioClient(); minioClient.removeObject( RemoveObjectArgs.builder() .bucket(bucketName) .object(objectName) .build()); logger.info(\"对象 \'\" + objectName + \"\' 成功从桶 \'\" + bucketName + \"\' 中删除.\"); } /** * 删除桶 * * @param bucketName 桶名称 * @throws Exception 如果操作失败 */ public void removeBucket(String bucketName) throws Exception { initMinioClient(); minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build()); logger.info(\"桶 \'\" + bucketName + \"\' 已删除.\"); } /** * 上传整个文件夹到 MinIO 的指定桶。 * 会递归遍历本地文件夹中的所有文件和子文件夹。 * MinIO 中的对象名称会保留原始文件的相对路径。 * * @param bucketName 目标桶名称 * @param localFolderPath 要上传的本地根文件夹的路径 * @param objectPrefix 在 MinIO 中存储的根前缀 (可选,例如 \"user_data/\")。如果为空,文件将直接上传到桶的根目录。 * @throws Exception 如果上传过程中发生错误 */ public void uploadFolder(String bucketName, String localFolderPath, String objectPrefix) throws Exception { initMinioClient(); createBucketIfNotExists(bucketName); // 确保桶存在 File baseFolder = new File(localFolderPath); if (!baseFolder.isDirectory()) { throw new IllegalArgumentException(\"提供的路径不是一个目录: \" + localFolderPath); } // 确保前缀以斜杠结尾,如果提供了的话 if (objectPrefix != null && !objectPrefix.isEmpty() && !objectPrefix.endsWith(\"/\")) { objectPrefix += \"/\"; } else if (objectPrefix == null) { objectPrefix = \"\"; // 如果没有提供前缀,则为空字符串 } logger.info(\"开始上传文件夹 \'\" + localFolderPath + \"\' 到桶 \'\" + bucketName + \"\',MinIO 前缀为 \'\" + objectPrefix + \"\'.\"); // 开始递归上传 uploadDirectoryRecursive(bucketName, baseFolder, objectPrefix); logger.info(\"文件夹 \'\" + localFolderPath + \"\' 上传完成。\"); } /** * 递归上传目录中的文件和子目录。 * * @param bucketName 目标桶名称 * @param directory 当前要处理的本地目录 * @param currentObjectPrefix 在 MinIO 中的当前对象前缀 * @throws Exception 如果上传过程中发生错误 */ private void uploadDirectoryRecursive(String bucketName, File directory, String currentObjectPrefix) throws Exception { File[] files = directory.listFiles(); if (files == null) { logger.warning(\"目录为空或不可读: \" + directory.getAbsolutePath()); return; } for (File file : files) { if (file.isDirectory()) { // 如果是子目录,递归调用 uploadDirectoryRecursive(bucketName, file, currentObjectPrefix + file.getName() + \"/\"); } else { // 如果是文件,上传到 MinIO String objectName = currentObjectPrefix + file.getName(); uploadFileInternal(bucketName, file, objectName); // 调用内部方法,避免重复检查桶 } } } /** * 内部方法:上传单个文件到 MinIO,不进行桶存在性检查。 * 主要供递归上传文件夹时调用。 * * @param bucketName 桶名称 * @param file 本地文件对象 * @param objectName 在 MinIO 中的对象名称 * @throws Exception 如果上传过程中发生错误 */ private void uploadFileInternal(String bucketName, File file, String objectName) throws Exception { try (FileInputStream fis = new FileInputStream(file)) { minioClient.putObject(  PutObjectArgs.builder() .bucket(bucketName) .object(objectName) .stream(fis, file.length(), -1) .build()); logger.info(\" - 上传文件: \" + file.getAbsolutePath() + \" -> MinIO: \" + bucketName + \"/\" + objectName); } catch (IOException e) { logger.severe(\"上传文件失败: \" + file.getAbsolutePath() + \", 错误: \" + e.getMessage()); throw e; // 重新抛出异常以便调用方处理 } } /** * 获取 MinIO 文件的预签名 URL。 * 这个 URL 具有时效性,可用于临时访问私有文件。 * * @param bucketName 桶名称 * @param objectName 对象名称(MinIO中存储的文件名) * @param expirySeconds URL 的有效期(秒),默认 MinIO SDK 是 7 天 (604800 秒)。 * @return 预签名 URL 字符串 * @throws Exception 如果获取失败 */ public String getPresignedObjectUrl(String bucketName, String objectName, int expirySeconds) throws Exception { initMinioClient(); // 校验桶是否存在,但通常获取预签名URL时,文件应该已经存在 // 如果文件不存在,生成的URL访问时会是404,但URL本身可以生成 if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) { throw new IllegalArgumentException(\"桶 \'\" + bucketName + \"\' 不存在.\"); } String url = minioClient.getPresignedObjectUrl( GetPresignedObjectUrlArgs.builder() .method(Method.GET) // 指定为 GET 请求,用于下载或直接访问 .bucket(bucketName) .object(objectName) .expiry(expirySeconds, TimeUnit.SECONDS) // 设置过期时间,单位为秒 .build()); logger.info(\"对象 \'\" + objectName + \"\' 的预签名URL已生成,有效期 \" + expirySeconds + \" 秒: \" + url); return url; } /** * 获取 MinIO 文件的公共 URL。 * 注意:这要求 MinIO 桶或对象设置为公共可读。 * 如果没有设置公共策略,该 URL 可能无法直接访问。 * * @param bucketName 桶名称 * @param objectName 对象名称(MinIO中存储的文件名) * @return 文件的公共 URL 字符串 */ public String getPublicObjectUrl(String bucketName, String objectName) { // 公共URL通常就是 MinIO URL + 桶名 + 对象名 // 例如:http://8.222.179.199:9000/detect-beetle/打印清单.jpg String publicUrl = minioUrl + \"/\" + bucketName + \"/\" + objectName; logger.info(\"对象 \'\" + objectName + \"\' 的公共URL: \" + publicUrl); return publicUrl; }}

4、controller 中进行测试

@RestController@RequestMapping(\"/api\")public class DetectionController { private final MinioUtil minioUtil; @Autowired public DetectionController(MinioUtil minioUtil) { this.minioUtil = minioUtil; } @PostMapping(\"test/minio\") public String testMinio(@RequestParam(\"bucketName\") String bucketName, @RequestParam(\"objectName\") String objectName,  @RequestParam(\"expirySeconds\") int expirySeconds) throws Exception { return minioUtil.getPresignedObjectUrl(bucketName, objectName,expirySeconds); }}

测试:
2025 docker 安装 minio 操作实践、java实践_minio社区版功能限制
这里成功返回了对应内容的url

其他方法,可自行测试。

总结

本篇文章主要是针对2025 MinIO更新后的操作实践,重点是进行常用操作的流程熟悉,需要注意的是代码中使用的是初始管理员凭证,如果需要对程序进行权限限制的话,可以使用拥有对应权限的账户(mc admin user add myminio newuser newpassword 默认没有任何权限)