开发文档:
MinIO Windows中文文档
MinIO Object Storage for Windows (英文文档)
1、准备工作
-
准备一个空的文件夹,用来存放minio相关的内容;
-
这里是在D盘创建一个minio的文件夹;
-
后续所有跟MinIO相关的内容,都是基于此文件夹
-
示例【D:\minio】
2、下载MinIO服务Windows下载和安装文档
Windows 下载链接
将这个应用程序移入提前准备的文件夹中【D:\minio\bin】
3.启动MinIO服务
不要双击运行!!!不要双击运行!!!不要双击运行!!!
打开cmd窗口,定位到【D:\minio\bin】文件夹中,输入下方命令,启动MinIO服务,不需要进行其他操作;
.\minio.exe server D:\minio\data
注意:
- 一定要定位到d:\minio\bin文件夹下;
- d:\minio\data 这个必须要填写,用来指定MinIO
- 在本地的存储路径; 这里用到data这个文件夹路径,可以提前创建好,没有创建的话也不要紧,执行上述命令时会自动帮我们创建;
说明: - API 列出了客户端可以访问MinIO S3 API的网络接口和端口; WebUI
- 列出了网络接口和端口,客户端可以通过这些接口和端口访问MinIO网页端的控制台。
- 9000端口为服务端口号 9001端口为控制台端口号
- 控制台访问
在浏览器中地址栏输入【http://127.0.0.1:9001】(以自己启动minio服务时的WebUI 端口为准,每次启动服务随机生成),访问本地的MinIO服务控制台;
输入用户名、密码进行登录,默认都是【minioadmin】(可以在cmd窗口中看到)
- 指定端口号启动服务
前面启动服务时,像控制台地址【http://127.0.0.1:9001】中的端口号,每次会自动生成,且不利于记忆。因此我们可以指定控制台端口号 9001;服务端口号9000;
.\minio.exe server D:\minio\data --console-address "127.0.0.1:9001" --address "127.0.0.1:9000"
4.设置用户名密码(cmd操作)
- 设置账号(至少3位):setx MINIO_ROOT_USER name
- 设置密码(至少8位):setx MINIO_ROOT_PASSWORD password
修改用户名:
setx MINIO_ROOT_USER admin
修改密码:
setx MINIO_ROOT_PASSWORD admin
重启服务
5、创建.bat文件启动服务
在【D:\minio】下创建start.txt文档
cmd /c "cd /d D:\minio\bin&& .\minio.exe server --address "127.0.0.1:9000" --console-address "127.0.0.1:9001" D:\minio\data"
注意:
- 【D:\minio\bin】:minio.exe服务的存放路径;
- 【.\minio.exe server 】:启动服务
- 【 --address “127.0.0.1:9000”】:指定服务端口号;
- 【 --console-address “127.0.0.1:9001”】:指定控制台端口号; 【 D:\minio\data 】:数据的存放路径;
保存后,更改这个文档的后缀名位.bat;双击运行这个文件,可以看到我们的服务已经正常启动了;
后台运行:在【start.bat】文件的最前面,加上下列命令:
@echo off
if "%1" == "h" goto begin
mshta vbscript:createobject("wscript.shell").run("""%~nx0"" h",0)(window.close)&&exit
:begin
REM
保存后,双击运行,cmd窗口只是一闪而过,打开任务管理器,会发现minio服务是启动的,但是原来的cmd窗口已经被隐藏了
6.MinIO基本操作
1、存储桶管理
桶:用来组织管理文件对象,类似于文件夹或者目录;MinIO中的桶可以存放任意数量个文件,也可以存放目录文件夹;
点击【Create Bucket】,创建桶;
输入桶的名称,命名规则:
- 长度必须介于3(最小)到63(最大)个字符之间;
- 只能由小写字母、数字、点(.)和连字符(-)组成;
- 不得包含两个相邻的句点或与连字符相邻的句点;
- 不得格式化为IP地址(例如192.168.5.4);
- 不能以前缀xn–开头;
- 不能以后缀-s3alias结尾;
- Bucket名称在分区内必须唯一;
这里创建名为【ssdl】的存储桶,其他都采用默认,点击【Create Bucket】创建即可;
2.对象管理
对象,就是二进制数据,包括各种图片、各类文档,以及音频、视频等等;
3.数据查看:
springboot整合
环境:springboot 2.1.6.RELEASE
MinIO版本8.2.1
依赖:
<!-- 操作minio的java客户端-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.1</version>
<exclusions>
<exclusion>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.12.0</version>
</dependency>
配置:
spring:
#minio配置
minio:
access-key:
secret-key:
url: http://127.0.0.1:9000 #访问地址
bucket-name: ssdl
key申请:
工具类:
package com.ssdl.baize.pub;
import java.util.HashMap;
/**
* @Description ajax结果
*/
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";
/**
* 初始化一个新创建的 AjaxResult 对象,使其表示一个空消息。
*/
public AjaxResult() {
}
/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param code 状态码
* @param msg 返回内容
*/
public AjaxResult(int code, String msg) {
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
}
/**
* 初始化一个新创建的 AjaxResult 对象
*
* @param code 状态码
* @param msg 返回内容
* @param data 数据对象
*/
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);
}
}
/**
* 返回成功消息
*
* @return 成功消息
*/
public static AjaxResult success() {
return AjaxResult.success("操作成功");
}
/**
* 返回成功数据
*
* @return 成功消息
*/
public static AjaxResult success(Object data) {
return AjaxResult.success("操作成功", data);
}
/**
* 返回成功消息
*
* @param msg 返回内容
* @return 成功消息
*/
public static AjaxResult success(String msg) {
return AjaxResult.success(msg, null);
}
/**
* 返回成功消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 成功消息
*/
public static AjaxResult success(String msg, Object data) {
return new AjaxResult(HttpStatus.SUCCESS, msg, data);
}
/**
* 返回警告消息
*
* @param msg 返回内容
* @return 警告消息
*/
public static AjaxResult warn(String msg) {
return AjaxResult.warn(msg, null);
}
/**
* 返回警告消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 警告消息
*/
public static AjaxResult warn(String msg, Object data) {
return new AjaxResult(HttpStatus.WARN, msg, data);
}
/**
* 返回错误消息
*
* @return 错误消息
*/
public static AjaxResult error() {
return AjaxResult.error("操作失败");
}
/**
* 返回错误消息
*
* @param msg 返回内容
* @return 错误消息
*/
public static AjaxResult error(String msg) {
return AjaxResult.error(msg, null);
}
/**
* 返回错误消息
*
* @param msg 返回内容
* @param data 数据对象
* @return 错误消息
*/
public static AjaxResult error(String msg, Object data) {
return new AjaxResult(HttpStatus.ERROR, msg, data);
}
/**
* 返回错误消息
*
* @param code 状态码
* @param msg 返回内容
* @return 错误消息
*/
public static AjaxResult error(int code, String msg) {
return new AjaxResult(code, msg, null);
}
/**
* 方便链式调用
*
* @param key 键
* @param value 值
* @return 数据对象
*/
@Override
public AjaxResult put(String key, Object value) {
super.put(key, value);
return this;
}
}
package com.ssdl.baize.pub;
/**
* @Description http请求状态
*/
public class HttpStatus {
/**
* 操作成功
*/
public static final int SUCCESS = 200;
/**
* 对象创建成功
*/
public static final int CREATED = 201;
/**
* 请求已经被接受
*/
public static final int ACCEPTED = 202;
/**
* 操作已经执行成功,但是没有返回数据
*/
public static final int NO_CONTENT = 204;
/**
* 资源已被移除
*/
public static final int MOVED_PERM = 301;
/**
* 重定向
*/
public static final int SEE_OTHER = 303;
/**
* 资源没有被修改
*/
public static final int NOT_MODIFIED = 304;
/**
* 参数列表错误(缺少,格式不匹配)
*/
public static final int BAD_REQUEST = 400;
/**
* 未授权
*/
public static final int UNAUTHORIZED = 401;
/**
* 访问受限,授权过期
*/
public static final int FORBIDDEN = 403;
/**
* 资源,服务未找到
*/
public static final int NOT_FOUND = 404;
/**
* 不允许的http方法
*/
public static final int BAD_METHOD = 405;
/**
* 资源冲突,或者资源被锁
*/
public static final int CONFLICT = 409;
/**
* 不支持的数据,媒体类型
*/
public static final int UNSUPPORTED_TYPE = 415;
/**
* 系统内部错误
*/
public static final int ERROR = 500;
/**
* 接口未实现
*/
public static final int NOT_IMPLEMENTED = 501;
/**
* 系统警告消息
*/
public static final int WARN = 601;
}
package com.ssdl.baize.pub;
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;
/**
* @Description minio配置
*/
@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();
}
}
package com.ssdl.baize.pub;
import io.minio.*;
import io.minio.errors.*;
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.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.ZonedDateTime;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @Description Minio工具类
*/
@Component
public class MinioUtils {
@Autowired
private MinioClient minioClient;
@Autowired
private MinioConfig configuration;
/**
* @param name 名字
* @return boolean
* @Description description: 判断bucket是否存在,不存在则创建
*/
public boolean existBucket(String name) {
boolean exists;
try {
exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
if (!exists) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
exists = true;
}
} catch (Exception e) {
e.printStackTrace();
exists = false;
}
return exists;
}
/**
* @param bucketName 存储bucket名称
* @return {@link Boolean }
* @Description 创建存储bucket
*/
public Boolean makeBucket(String bucketName) {
try {
minioClient.makeBucket(MakeBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* @param bucketName 存储bucket名称
* @return {@link Boolean }
* @Description 删除存储bucket
*/
public Boolean removeBucket(String bucketName) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* @param fileName 文件名称
* @param time 时间
* @return {@link Map }
* @Description 获取上传临时签名
*/
@SneakyThrows
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 (ErrorResponseException e) {
e.printStackTrace();
} catch (InsufficientDataException e) {
e.printStackTrace();
} catch (InternalException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (ServerException e) {
e.printStackTrace();
} catch (XmlParserException e) {
e.printStackTrace();
}
return null;
}
/**
* @param objectName 对象名称
* @param method 方法
* @param time 时间
* @param timeUnit 时间单位
* @return {@link String }
* @Description 获取上传文件的url
*/
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 (ErrorResponseException e) {
e.printStackTrace();
} catch (InsufficientDataException e) {
e.printStackTrace();
} catch (InternalException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (XmlParserException e) {
e.printStackTrace();
} catch (ServerException e) {
e.printStackTrace();
}
return null;
}
/**
* @param file 文件
* @param fileName 文件名称
* @Description 上传文件
*/
public void upload(MultipartFile file, String fileName) {
// 使用putObject上传一个文件到存储桶中。
try {
InputStream inputStream = file.getInputStream();
minioClient.putObject(PutObjectArgs.builder()
.bucket(configuration.getBucketName())
.object(fileName)
.stream(inputStream, file.getSize(), -1)
.contentType(file.getContentType())
.build());
} catch (ErrorResponseException e) {
e.printStackTrace();
} catch (InsufficientDataException e) {
e.printStackTrace();
} catch (InternalException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (ServerException e) {
e.printStackTrace();
} catch (XmlParserException e) {
e.printStackTrace();
}
}
/**
* @param objectName 对象名称
* @param time 时间
* @param timeUnit 时间单位
* @return {@link String }
* @Description 根据filename获取文件访问地址
*/
public String getUrl(String objectName, int time, TimeUnit timeUnit) {
String url = null;
try {
url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(configuration.getBucketName())
.object(objectName)
.expiry(time, timeUnit).build());
} catch (ErrorResponseException e) {
e.printStackTrace();
} catch (InsufficientDataException e) {
e.printStackTrace();
} catch (InternalException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidResponseException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (XmlParserException e) {
e.printStackTrace();
} catch (ServerException e) {
e.printStackTrace();
}
return url;
}
/**
* @param fileName
* @return {@link ResponseEntity }<{@link byte[] }>
* @Description description: 下载文件
*/
public ResponseEntity<byte[]> download(String fileName) {
ResponseEntity<byte[]> responseEntity = null;
InputStream in = null;
ByteArrayOutputStream out = null;
try {
in = minioClient.getObject(GetObjectArgs.builder().bucket(configuration.getBucketName()).object(fileName).build());
out = new ByteArrayOutputStream();
IOUtils.copy(in, out);
//封装返回值
byte[] bytes = out.toByteArray();
HttpHeaders headers = new HttpHeaders();
try {
headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
headers.setContentLength(bytes.length);
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setAccessControlExposeHeaders(Arrays.asList("*"));
responseEntity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return responseEntity;
}
/**
* @param objectFile 对象文件
* @return {@link String }
* @Description 根据文件名和桶获取文件路径
*/
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;
}
}
该代码是一个工具类,用于使用阿里云的对象存储服务(OSS)进行文件上传和下载。具体功能如下:
- getPolicy(String fileName, ZonedDateTime time):根据文件名和时间戳获取上传临时签名。
- getPolicyUrl(String objectName, Method method, int time, TimeUnit timeUnit):获取上传文件的url。
- upload(MultipartFile file, String fileName):将文件上传到OSS中。
- getUrl(String objectName, int time, TimeUnit timeUnit):获取文件的下载url。
- 代码中使用了枚举类型来定义不同的上传和下载方法。
创建Minio文件操作接口层
package com.ssdl.baize.controller;
import com.ssdl.baize.pub.AjaxResult;
import com.ssdl.baize.pub.MinioUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
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;
/**
* @Description minio文件上传控制器
*/
@RestController
@RequestMapping("/api")
@Api(tags = "OSS")
public class MinioFileUploadController {
@Autowired
private MinioUtils minioUtils;
/**
* @param file 文件
* @return {@link AjaxResult }
* @Description 上传文件
*/
@PostMapping("/upload")
@ApiOperation(value = "上传文件")
public AjaxResult uploadFile(@RequestParam("file") MultipartFile file) {
String fileName = file.getOriginalFilename();
minioUtils.upload(file, fileName);
return AjaxResult.success("上传成功");
}
/**
* @param fileName 文件名称
* @return {@link ResponseEntity }
* @Description dowload文件
*/
@GetMapping("/dowload")
@ApiOperation(value = "dowload文件")
public ResponseEntity dowloadFile(@RequestParam("fileName") String fileName) {
return minioUtils.download(fileName);
}
/**
* @param fileName 文件名称
* @return {@link AjaxResult }
* @Description 得到文件url
*/
@GetMapping("/getUrl")
@ApiOperation(value = "得到文件url")
public AjaxResult getFileUrl(@RequestParam("fileName") String fileName){
HashMap map=new HashMap();
map.put("FileUrl",minioUtils.getFileUrl(fileName));
return AjaxResult.success(map);
}
}
注意:如果遇到报错如下
okhttp3.Headers$Builder.addUnsafeNonAscii(Ljava/lang/String;Ljava/lang/String
降低 okhttp 版本
前面依赖以降低版本
测试:
上传文件
下载文件
url链接