Win10安装Minio及SpringBoot集成Minio
作者:mmseoamin日期:2023-12-25

简介

Minio是一款开源的云存储服务器,旨在为企业和个人用户提供简单、易用、高性能的对象存储服务。与传统的云存储产品不同,Minio是构建在HTTP服务之上的对象存储服务器,它支持AWS S3 API,可以为用户提供简单易用的对象存储服务,可用于存储各种类型的文件,包括图片、音频、视频等。

一、下载

我们需要先到minio官网进行下载:下载地址

二、运行启动minio

下载完成后,由于minio.exe是不能直接执行的,需要cmd进入下载目录执行命令:

minio.exe server D:\data

Win10安装Minio及SpringBoot集成Minio,第1张出现以上界面说明启动成功,打开浏览器访问: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文件,内容如下:


    minio-server
    minio-server
    minio文件存储服务器
    
    
    %BASE%\minio.exe
    server "%BASE%\data"
    %BASE%\logs
    
      10240
      yyyyMMdd
      00:00:00
      5
      yyyyMMdd
    

需在环境变量中新建MINIO_HOME,值就是存放minio.exe的目录

打开cmd进入到目录下,运行命令安装minio服务:

minio-server.exe install

如果想卸载minio-server,运行:

minio-server.exe uninstall

访问http://localhost:9000,出现minio的管理后台,说明服务安装成功

四、Spring Boot集成Minio

1.首先引入依赖

    io.minio
    minio
    8.4.3
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 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 removeObjects(List objectNames) {
        List 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 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 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 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 downloadFile(String fileName) {
        return minioUtils.getObject(fileName);
    }
    /**
     * 下载多个文件到压缩包返回
     * @param fileName 要下载的文件名数组
     * @return 压缩流
     */
    @GetMapping(value = "/package")
    public ResponseEntity 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);
    }
}

接下来就可以跟接口愉快得玩耍了。