lvlinguang

一、minio简介

MinIO 是在 GNU Affero 通用公共许可证 v3.0 下发布的高性能对象存储。 它是与 Amazon S3 云存储服务兼容的 API,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等

二、minio安装

参考我的另一篇文章:https://www.cnblogs.com/lvlinguang/p/15770479.html

一、java中使用

1、pom文件引用

<!-- minio 文件服务客户端 -->
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>6.0.11</version>
</dependency>

2、添加配置文件

  • url:minio服务器的接口地址,不是访问地址
  • accessKey/secretKey:登录minio系统,新建Service Accounts
config:
  minio:
    url: http://192.168.3.15:9000
    accessKey: 66SBZWYDSO0DZRSE1U3T
    secretKey: S+p8mWE8aykZ0YsRtC0ef35qUS7fUbkITITJdjS6

3、注册MinioClient

@Data
@Configuration
@ConfigurationProperties(prefix = "config.minio")
public class MinioConfig {
    /**
     * minio 服务地址 http://ip:port
     */
    private String url;

    /**
     * 用户名
     */
    private String accessKey;

    /**
     * 密码
     */
    private String secretKey;

    @SneakyThrows
    @Bean
    @RefreshScope
    public MinioClient minioClient(){
        return new MinioClient(url, accessKey, secretKey);
    }
}

4、minio工具类

/**
 * minio工具类
 *
 * @author lvlinguang
 * @date 2022-01-07 12:26
 * @see http://docs.minio.org.cn/docs/master/java-client-api-reference
 */
@Component
public class MinioUtils {

    @Autowired
    private MinioClient client;

    /**
     * 创建bucket
     *
     * @param bucketName
     */
    @SneakyThrows
    public void createBucket(String bucketName) {
        if (!client.bucketExists(bucketName)) {
            client.makeBucket(bucketName);
        }
    }

    /**
     * 获取所有bucket
     *
     * @return
     */
    @SneakyThrows
    public List<Bucket> listAllBuckets() {
        return client.listBuckets();
    }

    /**
     * bucket详情
     *
     * @param bucketName 名称
     * @return
     */
    @SneakyThrows
    public Optional<Bucket> getBucket(String bucketName) {
        return client.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
    }

    /**
     * 删除bucket
     *
     * @param bucketName 名称
     */
    @SneakyThrows
    public void removeBucket(String bucketName) {
        client.removeBucket(bucketName);
    }

    /**
     * 上传文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param stream     文件流
     * @throws Exception
     */
    @SneakyThrows
    public void uploadFile(String bucketName, String objectName, InputStream stream) throws Exception {
        this.uploadFile(bucketName, objectName, stream, (long) stream.available(), "application/octet-stream");
    }

    /**
     * 上传文件
     *
     * @param bucketName  bucket名称
     * @param objectName  文件名称
     * @param stream      文件流
     * @param size        大小
     * @param contextType 类型
     * @throws Exception
     */
    @SneakyThrows
    public void uploadFile(String bucketName, String objectName, InputStream stream, long size, String contextType) throws Exception {
        //如果bucket不存在,则创建
        this.createBucket(bucketName);
        client.putObject(bucketName, objectName, stream, size, null, null, contextType);
    }

    /**
     * 获取文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @return
     */
    @SneakyThrows
    public InputStream getFile(String bucketName, String objectName) {
        return client.getObject(bucketName, objectName);
    }

    /**
     * 根据文件前置查询文件
     *
     * @param bucketName bucket名称
     * @param prefix     前缀
     * @param recursive  是否递归查询
     * @return
     */
    @SneakyThrows
    public List<MinioItem> listAllFileByPrefix(String bucketName, String prefix, boolean recursive) {
        List<MinioItem> objectList = new ArrayList<>();
        Iterable<Result<Item>> objectsIterator = client
                .listObjects(bucketName, prefix, recursive);
        while (objectsIterator.iterator().hasNext()) {
            objectList.add(new MinioItem(objectsIterator.iterator().next().get()));
        }
        return objectList;
    }

    /**
     * 删除文件
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     */
    @SneakyThrows
    public void removeFile(String bucketName, String objectName) {
        client.removeObject(bucketName, objectName);
    }

    /**
     * 获取文件外链
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @param expires    过期时间 <=7
     * @return
     */
    @SneakyThrows
    public String getFileURL(String bucketName, String objectName, Integer expires) {
        return client.presignedGetObject(bucketName, objectName, expires);
    }

    /**
     * 获取文件信息
     *
     * @param bucketName bucket名称
     * @param objectName 文件名称
     * @return
     */
    @SneakyThrows
    public ObjectStat getFileInfo(String bucketName, String objectName) {
        return client.statObject(bucketName, objectName);
    }
}

5、文件操作类

/**
 * 系统文件工具类
 *
 * @author lvlinguang
 * @date 2021-02-28 12:30
 */
@Component
public class SysFileUtils {

    /**
     * 文件服务器中的目录分隔符
     */
    public static final String SEPRETOR = "/";

    @Autowired
    private MinioUtils minioUtils;

    /**
     * 文件上传
     *
     * @param object     文件对你
     * @param bucketName bucket名称
     * @return
     */
    @SneakyThrows
    public MinioObject uploadFile(MultipartFile object, String bucketName) {
        return this.uploadFile(object.getInputStream(), bucketName, object.getOriginalFilename());
    }

    /**
     * 文件上传
     *
     * @param object     文件对你
     * @param bucketName bucket名称
     * @param fileName   文件名
     * @return
     */
    @SneakyThrows
    public MinioObject uploadFile(MultipartFile object, String bucketName, String fileName) {
        return this.uploadFile(object.getInputStream(), bucketName, fileName);
    }

    /**
     * 文件上传
     *
     * @param object         文件对你
     * @param bucketName     bucket名称
     * @param randomFileName 文件名是否随机(是:按年/月/日/随机值储存,否:按原文件名储存)
     * @return
     */
    @SneakyThrows
    public MinioObject uploadFile(MultipartFile object, String bucketName, Boolean randomFileName) {
        //文件名
        String fileName = object.getOriginalFilename();
        if (randomFileName) {
            //扩展名
            String extName = FileUtil.extName(object.getOriginalFilename());
            if (StrUtil.isNotBlank(extName)) {
                extName = StrUtil.DOT + extName;
            }
            //新文件名
            fileName = randomFileName(extName);
        }
        return this.uploadFile(object.getInputStream(), bucketName, fileName);
    }

    /**
     * 文件上传
     *
     * @param object     文件对你
     * @param bucketName bucket名称
     * @param fileName   文件名
     * @return
     */
    @SneakyThrows
    public MinioObject uploadFile(InputStream object, String bucketName, String fileName) {
        try {
            minioUtils.uploadFile(bucketName, fileName, object);
            return new MinioObject(minioUtils.getFileInfo(bucketName, fileName));
        } catch (Exception e) {
            throw new Exception(e);
        } finally {
            if (object != null) {
                object.close();
            }
        }
    }

    /**
     * 下载文件
     *
     * @param response response
     * @param url      文件地址(/bucketName/fileName)
     */
    public void downloadFile(HttpServletResponse response, String url) {
        final String bucketName = getBucketName(url);
        final String filePath = getFilePath(url);
        this.downloadFile(response, bucketName, filePath);
    }

    /**
     * 下载文件
     *
     * @param response response
     * @param bucket   bucket名称
     * @param fileName 文件名
     */
    public void downloadFile(HttpServletResponse response, String bucket, String fileName) {
        try (InputStream inputStream = minioUtils.getFile(bucket, fileName)) {
            if ("jpg".equals(FileUtil.extName(fileName))) {
                response.setContentType("image/jpeg");
            } else if ("png".equals(FileUtil.extName(fileName))) {
                response.setContentType("image/png");
            } else {
                response.setContentType("application/octet-stream; charset=UTF-8");
            }
            IoUtil.copy(inputStream, response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        }
    }

    /**
     * 获取链接地址的 文件名
     *
     * @param bucketFileUrl
     * @return
     */
    public static String getFilePath(String bucketFileUrl) {
        if (bucketFileUrl == null) {
            return null;
        }
        //去掉第一个分割符
        if (bucketFileUrl.startsWith(SEPRETOR)) {
            bucketFileUrl = bucketFileUrl.substring(1);
        }
        return bucketFileUrl.substring(bucketFileUrl.indexOf(SEPRETOR) + 1);
    }


    /**
     * 获取链接地址的 bucketName
     *
     * @param bucketFileUrl 地址(/{bucketName}/{path}/{fileName})
     * @return
     */
    public static String getBucketName(String bucketFileUrl) {
        if (bucketFileUrl == null) {
            return null;
        }
        //去掉第一个分割符
        if (bucketFileUrl.startsWith(SEPRETOR)) {
            bucketFileUrl = bucketFileUrl.substring(1);
        }
        return bucketFileUrl.substring(0, bucketFileUrl.indexOf(SEPRETOR));
    }

    /**
     * 生成新的文件名
     *
     * @param extName 扩展名
     * @return
     */
    public static String randomFileName(String extName) {
        LocalDate now = LocalDate.now();
        return now.getYear() + SEPRETOR +
                getFullNumber(now.getMonthValue()) + SEPRETOR +
                getFullNumber(now.getDayOfMonth()) + SEPRETOR +
                UUID.randomUUID().toString().replace("-", "") + extName;
    }

    /**
     * 得到数字全称,带0
     *
     * @param number
     * @return
     */
    public static String getFullNumber(Integer number) {
        if (number < 10) {
            return "0" + number;
        }
        return number.toString();
    }
}

5、返回包装类

@Data
public class MinioItem {

    private String objectName;
    private Date lastModified;
    private String etag;
    private Long size;
    private String storageClass;
    private Owner owner;
    private String type;

    public MinioItem(Item item) {
        this.objectName = item.objectName();
        this.lastModified = item.lastModified();
        this.etag = item.etag();
        this.size = (long) item.size();
        this.storageClass = item.storageClass();
        this.owner = item.owner();
        this.type = item.isDir() ? "directory" : "file";
    }
}

@Data
public class MinioObject {

    private String bucketName;
    private String name;
    private Date createdTime;
    private Long length;
    private String etag;
    private String contentType;

    public MinioObject(ObjectStat os) {
        this.bucketName = os.bucketName();
        this.name = os.name();
        this.createdTime = os.createdTime();
        this.length = os.length();
        this.etag = os.etag();
        this.contentType = os.contentType();
    }
}

6、控制器调用

@RestController
@RequestMapping("/file")
public class FileController {

    public static final String MAPPING = "/file";

    @Autowired
    private SysFileUtils sysFileUtils;

    @PostMapping("/upload")
    public ApiResponse<MinioObject> upload(@RequestPart("file") MultipartFile file, @RequestParam("bucketName") String bucketName) {
        final MinioObject minioObject = sysFileUtils.uploadFile(file, bucketName, true);
        return ApiResponse.ok(minioObject);
    }

    @GetMapping("/info/**")
    public void getFile(HttpServletRequest request, HttpServletResponse response) {
        final String uri = request.getRequestURI();
        String fullName = uri.replace(MAPPING + "/info", "");
        sysFileUtils.downloadFile(response, fullName);
    }
}

7、访问测试

  • 上传测试

-下载/访问测试

地址:http://192.168.3.15:30061/info/avatar/2022/01/07/41aaeb9f56e64c10971b2d9c675ce8fe.jpg

相关文章: