SpringBoot:SpringBoot集成Minio+KkFileView实现所有文档格式预览功能

前言

博主做项目时,存储文件使用的是Minio,各类格式文件都有(图片,pdf,word,excel等等),因为项目需求这些文档能进行预览,全部交给前端实现需要各种组件支撑,这无疑会加大前端的开发量,所以博主在网上搜索大量解决方法,最终找到这种可以实现方案

具体的kkFileView的介绍和部署可以看我的另一篇文章:https://www.cnblogs.com/nhdlb/p/18541242

pom文件

<dependencies>
    <!--  springWeb依赖  -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <scope>provided</scope>
        <optional>true</optional>
    </dependency>
    <!--  minio依赖  -->
    <dependency>
        <groupId>io.minio</groupId>
        <artifactId>minio</artifactId>
    </dependency>
    <!--  lombok依赖  -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

配置信息

minio:
  endpoint: http://*.*.*.*:9000/
  access-key: -------------------
  secret-key: ---------------------------------------
  bucket-name: portal-platform
  ## 自定义配置Https访问路径的参数
  httpConf: https://*.*.*:10000/minio/

# 文件预览软件访问路径
kkfileview:
  # 访问地址
  server: https://*.*.*:10000/kkfileview/
  # 单文件预览-路径拼接
  single: onlinePreview
  # 多图片预览-路径拼接
  images: picturesPreview

工具类

kkfileview配置类

@Component
@ConfigurationProperties(prefix = "kkfileview")
public class KkfileviewProperties {

    // IP访问路径
    private String server;
    // 单文件预览-路径拼接
    private String single;
    // 多图片预览-路径拼接
    private String images;

    public void setServer(String server) {
        this.server = server;
    }

    public void setSingle(String single) {
        this.single = single;
    }

    public void setImages(String images) {
        this.images = images;
    }

    public String getServer() {
        return server;
    }

    public String getSingle() {
        return single;
    }

    public String getImages() {
        return images;
    }
}

minio配置类

@Component
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {

    private String endpoint;

    private String accessKey;

    private String secretKey;

    private String bucketName;
    // 自定义配置Https访问路径的参数
    private String httpConf;

    public String getEndpoint() {
        return endpoint;
    }

    public void setEndpoint(String endpoint) {
        this.endpoint = endpoint;
    }

    public String getAccessKey() {
        return accessKey;
    }

    public void setAccessKey(String accessKey) {
        this.accessKey = accessKey;
    }

    public String getSecretKey() {
        return secretKey;
    }

    public void setSecretKey(String secretKey) {
        this.secretKey = secretKey;
    }

    public String getBucketName() {
        return bucketName;
    }

    public void setBucketName(String bucketName) {
        this.bucketName = bucketName;
    }

    public String getHttpConf() {
        if(httpConf.charAt(httpConf.length()-1) != '/'){
            return httpConf+"/";
        }
        return httpConf;
    }

    public void setHttpConf(String httpConf) {
        this.httpConf = httpConf;
    }
}

minio链接注入Bean

@Configuration
public class MinioConfiguration {

    @Resource
    private MinioProperties minioProperties;

    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(minioProperties.getEndpoint())
                .credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
                .build();
    }

    @Bean
    public String currentMinioBucketName() {
        return minioProperties.getBucketName();
    }

}

Minio工具类

@Service
public class MinioService {

    @Resource
    private MinioClient minioClient;

    @Resource
    private String bucketName;

    /**
     * 判断存储桶是否存在
     *
     * @return boolean
     */
    @SneakyThrows
    public boolean isBucketExists() {
        return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
    }

    /**
     * 创建存储桶
     */
    @SneakyThrows
    public void createBucket() {
        if (!isBucketExists()) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    @SneakyThrows
    public List<Item> getFolderItems(String folderPath) throws Exception {
        Iterable<Result<Item>> objects = minioClient.listObjects(ListObjectsArgs.builder()
                .bucket(bucketName)
                .prefix(folderPath)
                .recursive(true)
                .build());
        for (Result<Item> object : objects) {
            Item item = object.get();
            System.out.println("item.objectName() = " + item.objectName());
//            if (item.isDir() && item.objectName().replace("/", "").equals(dirName)) {
//                exists = true;
//            }
        }
        return null;
    }

    /**
     * 判断目录是否存在
     *
     * @param dirName 目录名称
     * @return boolean
     */
    @SneakyThrows
    public boolean isDirExists(String dirName) {
        boolean exists = false;

        Iterable<Result<Item>> objects = minioClient.listObjects(ListObjectsArgs.builder()
                .bucket(bucketName)
                .prefix(dirName)
                .recursive(false)
                .build()
        );

        for (Result<Item> object : objects) {
            Item item = object.get();
            System.out.println("item.objectName() = " + item.objectName());
            if (item.isDir() && item.objectName().replace("/", "").equals(dirName)) {
                exists = true;
            }
        }

        return exists;
    }

    /**
     * 判断文件是否存在
     *
     * @param objectName
     * @return
     */
    public boolean isObjectExists(String objectName) {
        try {
            minioClient.statObject(StatObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build()
            );

            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取文件流
     *
     * @param objectName 文件名
     * @return 二进制流
     */
    @SneakyThrows
    public InputStream getObject(String objectName) {
        return minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build());
    }

    /**
     * 断点下载
     *
     * @param objectName 文件名称
     * @param offset     起始字节的位置
     * @param length     要读取的长度
     * @return 二进制流
     */
    @SneakyThrows
    public InputStream getObject(String objectName, long offset, long length) {
        return minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .offset(offset)
                        .length(length)
                        .build());
    }

    /**
     * 使用MultipartFile进行文件上传
     *
     * @param file        文件名
     * @param objectName  对象名
     * @param contentType 类型
     * @return ObjectWriteResponse
     */
    @SneakyThrows
    public ObjectWriteResponse uploadFile(MultipartFile file, String objectName, String contentType) {
        InputStream inputStream = file.getInputStream();
        try {
            return minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .contentType(contentType)
                            .stream(inputStream, inputStream.available(), -1)
                            .build());
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    /**
     * 使用File进行文件上传
     *
     * @param file        文件名
     * @param objectName  对象名
     * @param contentType 类型
     * @return ObjectWriteResponse
     */
    @SneakyThrows
    public ObjectWriteResponse uploadFile(File file, String objectName, String contentType) {
        InputStream inputStream = new FileInputStream(file);
        try {
            return minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketName)
                            .object(objectName)
                            .contentType(contentType)
                            .stream(inputStream, inputStream.available(), -1)
                            .build());
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

    /**
     * 上传本地文件
     *
     * @param objectName 对象名称
     * @param fileName   本地文件路径
     * @return ObjectWriteResponse
     */
    @SneakyThrows
    public ObjectWriteResponse uploadFile(String objectName, String fileName) {
        return minioClient.uploadObject(
                UploadObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .filename(fileName)
                        .build());
    }

    /**
     * 通过流上传文件
     *
     * @param objectName  文件对象
     * @param inputStream 文件流
     * @return ObjectWriteResponse
     */
    @SneakyThrows
    public ObjectWriteResponse uploadFile(String objectName, InputStream inputStream) {
        return minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .stream(inputStream, inputStream.available(), -1)
                        .build());
    }

    /**
     * 创建文件夹或目录
     *
     * @param objectName 目录路径
     * @return ObjectWriteResponse
     */
    @SneakyThrows
    public ObjectWriteResponse createDir(String objectName) {
        return minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .stream(new ByteArrayInputStream(new byte[]{}), 0, -1)
                        .build());
    }

    /**
     * 获取文件信息, 如果抛出异常则说明文件不存在
     *
     * @param objectName 文件名称
     * @return String
     */
    @SneakyThrows
    public String getFileStatusInfo(String objectName) {
        return minioClient.statObject(
                StatObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build()).toString();
    }

    /**
     * 拷贝文件
     *
     * @param objectName    文件名
     * @param srcBucketName 目标存储桶
     * @param srcObjectName 目标文件名
     */
    @SneakyThrows
    public ObjectWriteResponse copyFile(String objectName, String srcBucketName, String srcObjectName) {
        return minioClient.copyObject(
                CopyObjectArgs.builder()
                        .source(CopySource.builder().bucket(bucketName).object(objectName).build())
                        .bucket(srcBucketName)
                        .object(srcObjectName)
                        .build());
    }

    /**
     * 文件移动
     * @param objectName 旧文件名
     * @param newObjectName 新文件名
     */
    @SneakyThrows
    public void moveFile(String objectName, String newObjectName) {
        copyFile(objectName, bucketName, newObjectName);
        removeFile(objectName);
    }

    /**
     * 删除文件
     *
     * @param objectName 文件名称
     */
    @SneakyThrows
    public void removeFile(String objectName) {
        minioClient.removeObject(
                RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build());
    }

    /**
     * 获取有效时长的文件外链
     *
     * @param objectName 文件名
     * @param expires    过期时间 <=7 秒 (外链有效时间(单位:秒))
     * @return url
     */
    @SneakyThrows
    public String getPreSignedObjectUrl(String objectName, Integer expires) {
        GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
                .expiry(expires)
                .bucket(bucketName)
                .object(objectName)
                .method(Method.GET)
                .build();
        return minioClient.getPresignedObjectUrl(args);
    }

    /**
     * 获得永不失效的文件外链
     *
     * @param objectName 对象名称
     * @return url
     */
    @SneakyThrows
    public String getPublicObjectUrl(String objectName) {
        GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .method(Method.GET)
                .build();

        URI uri = new URI(minioClient.getPresignedObjectUrl(args));
        String path = uri.getPath().replaceAll("//", "/");
        return new URI(uri.getScheme(), uri.getAuthority(), path, null, null).toString();
    }

}

生成访问链接

@RestController
@RequestMapping("/sysfile")
@RequiredArgsConstructor
public class SysFileController {
    
    // Minio工具类
    private final MinioService minioService;
    // Minio配置文件
    private final MinioProperties minioProperties;
    // Kkfileview配置文件
    private final KkfileviewProperties kkfileviewProperties;
    
    /**
     * 上传文件
     *
     * @param file 文件
     * @param type
     * @return {@link String}
     */
    @Operation(summary = "文件上传")
    @GetMapping("/file/upload")
    public String uploadFile(@RequestParam("file") MultipartFile file) {
        String url = null;
        try {
            // 文件名
            String filename = file.getOriginalFilename();
            // minio 上传文件
            ObjectWriteResponse objectWriteResponse = minioService.uploadFile(file, filename, file.getContentType());
            // 获得永不失效的文件外链
            url = minioService.getPublicObjectUrl(objectWriteResponse.object());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return url;
    }
    
    /**
     * 文件预览
     * @param url minio文件外链
     */
    @Operation(summary = "文件预览")
    @GetMapping("/file/preview")
    public String previewFile(@RequestParam("url") String url) {
        return kkfileviewProperties.getServer() + kkfileviewProperties.getSingle() + "?url="
                + URLEncoder.encode(url, StandardCharsets.UTF_8);
    }

}

完成!!!!

 

posted @ 2024-11-12 10:53  怒吼的萝卜  阅读(1508)  评论(0)    收藏  举报