简介
Minio是一款开源的云存储服务器,旨在为企业和个人用户提供简单、易用、高性能的对象存储服务。与传统的云存储产品不同,Minio是构建在HTTP服务之上的对象存储服务器,它支持AWS S3 API,可以为用户提供简单易用的对象存储服务,可用于存储各种类型的文件,包括图片、音频、视频等。
一、下载
我们需要先到minio官网进行下载:下载地址
二、运行启动minio
下载完成后,由于minio.exe是不能直接执行的,需要cmd进入下载目录执行命令:
minio.exe server D:\data
出现以上界面说明启动成功,打开浏览器访问:http://localhost:9000,就可以看到minio自带的管理后台了,登录的用户名和密码都是minioadmin
三、将minio集成到windows服务中
由于每次都需要打开cmd窗口运行命令后才能访问minio,关掉就不能访问了,非常不方便,所以我们可以将minio集成到服务中,这样就不用每次都用cmd启动了。
集成minio到服务需要下载WinSW.exe,下载地址
下载完成后将WinSW.exe和nimio.exe放到一个自定义的目录下,可以将WinSW.exe重命名成一个喜欢的名字,比如:minio-server.exe
在目录下新建一个minio-server.xml文件,内容如下:
<service>
<id>minio-server</id>
<name>minio-server</name>
<description>minio文件存储服务器</description>
<!-- 可设置环境变量 -->
<env name="MINIO_HOME" value="%BASE%"/>
<executable>%BASE%\minio.exe</executable>
<arguments>server "%BASE%\data"</arguments>
<logpath>%BASE%\logs</logpath>
<log mode="roll-by-size-time">
<sizeThreshold>10240</sizeThreshold>
<pattern>yyyyMMdd</pattern>
<autoRollAtTime>00:00:00</autoRollAtTime>
<zipOlderThanNumDays>5</zipOlderThanNumDays>
<zipDateFormat>yyyyMMdd</zipDateFormat>
</log>
</service>
需在环境变量中新建MINIO_HOME,值就是存放minio.exe的目录
打开cmd进入到目录下,运行命令安装minio服务:
minio-server.exe install
如果想卸载minio-server,运行:
minio-server.exe uninstall
访问http://localhost:9000,出现minio的管理后台,说明服务安装成功
四、Spring Boot集成Minio
1.首先引入依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.4.3</version>
</dependency>
2.在yml文件中添加配置
minio:
url: http://localhost:9000
access-key: minioadmin
secret-key: minioadmin
bucket: test
3.添加minio配置类
@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
private String accessKey;
private String secretKey;
private String url;
@Bean
public MinioClient minioClient() {
try {
return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build();
}catch (Exception e) {
throw new RuntimeException("初始化MinioClient失败", e);
}
}
}
4.编写工具类
@Component
public class MinioUtils {
@Resource
private MinioClient minioClient;
@Value("${minio.bucket}")
private String bucketName;
/**
* 判断存储桶是否存在
*
* @return 桶是否存在
*/
public boolean bucketExists() {
try {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
throw new RuntimeException("桶不存在", e);
}
}
/**
* 创建桶
*/
public void makeBucket() {
try {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
throw new RuntimeException("创建桶失败", e);
}
}
/**
* 上传单个对象
*
* @param file 文件
* @return 上传成功
*/
public SysResult putObject(MultipartFile file) {
if (file == null || file.isEmpty())
throw new IllegalArgumentException("文件不能为空");
try (InputStream inputStream = file.getInputStream()){
//检查存储桶是否存在
if (!bucketExists())
makeBucket();
// 获取文件名
String fileName = file.getOriginalFilename();
assert fileName != null;
// 生成随机的对象名称
String objectName = IdUtils.simpleUUID() + "." + FileUtil.getFileSuffix(fileName);
// 添加自定义的元数据
Map<String, String> meta = new HashMap<>();
meta.put("file-name", fileName);
// 添加对象
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.userMetadata(meta)
.stream(inputStream, file.getSize(), -1)
.contentType(file.getContentType())
.build());
return SysResult.success("上传成功", objectName);
} catch (Exception e) {
throw new RuntimeException("上传失败", e);
}
}
/**
* 上传多个对象
*
* @param files 文件数组
* @return 上传成功
*/
public SysResult putObject(MultipartFile[] files) {
for (MultipartFile file : files) {
putObject(file);
}
return SysResult.success("上传成功");
}
/**
* 删除一个对象
*
* @param objectName 对象名称
* @return 是否删除成功
*/
public boolean removeObject(String objectName) {
if (bucketExists()) {
try {
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
return true;
} catch (Exception e) {
throw new RuntimeException("删除失败", e);
}
}
return false;
}
/**
* 删除多个对象,返回删除失败的对象列表
*
* @param objectNames 对象List
* @return 删除失败的对象List
*/
public List<String> removeObjects(List<String> objectNames) {
List<String> deleteErrorNames = new ArrayList<>();
if (bucketExists()) {
for (String objectName : objectNames) {
if (!removeObject(objectName)) {
deleteErrorNames.add(objectName);
}
}
}
return deleteErrorNames;
}
/**
* 获取对象的元数据
*
* @param objectName 对象名称
* @return 元数据
*/
public SysResult statObject(String objectName) {
try {
if (bucketExists()) {
StatObjectResponse response = minioClient.statObject(StatObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
// 输出对象的元数据信息
Map<String, String> metadata = response.userMetadata();
// 返回
return SysResult.data(metadata);
}
} catch (Exception e) {
throw new RuntimeException("获取元数据失败", e);
}
return null;
}
/**
* 获取文件的临时访问路径
*
* @param objectName 对象名称
* @return 临时访问url
*/
public String getObjectUrl(String objectName) {
String url = "";
try {
if (bucketExists()) {
// 设置预签名URL的有效期(以秒为单位)
int expiresInSeconds = 3600; // 一小时
url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.method(Method.GET) //只能get请求访问
.bucket(bucketName)
.object(objectName)
.expiry(expiresInSeconds) // 设置有效时间
.build());
}
} catch (Exception e) {
throw new RuntimeException("获取失败", e);
}
return url;
}
/**
* 获取(下载)单个文件
*
* @param fileName 文件名
* @return 输出流
*/
public ResponseEntity<ByteArrayResource> getObject(String fileName){
// 获取输入流
try (InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
.bucket(bucketName)
.object(fileName).build())) {
byte[] byteArray = IOUtils.toByteArray(inputStream);
ByteArrayResource resource = new ByteArrayResource(byteArray);
//设置请求头
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName);
//开始下载
return ResponseEntity.ok()
.headers(headers)
.contentLength(byteArray.length)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
}catch (Exception e) {
throw new RuntimeException("下载失败", e);
}
}
/**
* 获取(下载)多个文件,写进zip压缩包返回
*
* @param objectNames 资源名数组
* @return 输出流
*/
public ResponseEntity<byte[]> getObjectList(String[] objectNames) {
// 创建一个输出流,将文件写入压缩包
try (ByteArrayOutputStream aos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(aos)) {
for (String objectName : objectNames) {
// 获取对象
InputStream inputStream = minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
// 创建一个新的ZIP条目,并将文件写入压缩包
ZipEntry zipEntry = new ZipEntry(objectName);
zos.putNextEntry(zipEntry);
byte[] buffer = new byte[8192];
int length;
while ((length = inputStream.read(buffer)) > 0) {
zos.write(buffer, 0, length);
}
// 关闭当前的ZIP条目
zos.closeEntry();
inputStream.close();
}
// 关闭 ZIP 输出流
zos.close();
// 将压缩包的字节数组作为响应体返回
byte[] zipBytes = aos.toByteArray();
//设置请求头
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=output.zip");
//开始下载
return ResponseEntity.ok()
.headers(headers)
.contentLength(zipBytes.length)
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(zipBytes);
} catch (Exception e) {
throw new RuntimeException("文件下载失败", e);
}
}
}
5.编写控制器
@RestController
@RequestMapping(value = "/file")
public class FileController {
@Resource
private MinioUtils minioUtils;
/**
* 获取元数据
* @param fileName 文件名
* @return 元数据
*/
@GetMapping(value = "/stat")
public SysResult stat(String fileName) {
return minioUtils.statObject(fileName);
}
/**
* 根据文件名获取临时访问URL
* @param fileName 文件名
* @return 临时访问URL
*/
@GetMapping(value = "/getUrl")
public SysResult get(String fileName) {
return SysResult.data(minioUtils.getObjectUrl(fileName));
}
/**
* 下载单个文件
*
* @param fileName 文件名
* @return 流
*/
@GetMapping(value = "/download")
public ResponseEntity<ByteArrayResource> downloadFile(String fileName) {
return minioUtils.getObject(fileName);
}
/**
* 下载多个文件到压缩包返回
* @param fileName 要下载的文件名数组
* @return 压缩流
*/
@GetMapping(value = "/package")
public ResponseEntity<byte[]> list(String[] fileName) {
return minioUtils.getObjectList(fileName);
}
/**
* 上传单个文件
* @param file 文件
* @return 是否上传成功
*/
@PostMapping(value = "/upload")
public SysResult upload(@RequestParam("file") MultipartFile file) {
return minioUtils.putObject(file);
}
/**
* 上传多个文件
*
* @param file 文件数组
* @return 上传成功
*/
@PostMapping(value = "/uploadList")
public SysResult uploadList(@RequestParam("file") MultipartFile[] file) {
return minioUtils.putObject(file);
}
/**
* 删除单个文件
*
* @param fileName 文件名
* @return 删除成功
*/
@GetMapping(value = "/remove")
public SysResult remove(String fileName) {
minioUtils.removeObject(fileName);
return SysResult.success("删除成功", fileName);
}
}
接下来就可以跟接口愉快得玩耍了。