minio檔案上傳與下載

呂林光發表於2022-01-07

一、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

相關文章