美文网首页
SpringBoot整合分布式文件系统

SpringBoot整合分布式文件系统

作者: liushiping | 来源:发表于2023-07-12 10:52 被阅读0次

    一.文件本地上传

    1.1 文件上传目录

    服务端接收上传的目的是提供文件的访问服务,那么对于SpringBoot而言,有哪些可以提供文件访问的静态资源目录呢?

    • classpath:/META-INF/resources/ ,
    • classpath:/static/ ,
    • classpath:/public/ ,
    • `classpath:/resources/

    以上目录都在工程里面,用来存储动态上传的文件是有很多问题的。spring boot 为我们提供了使用spring.web.resources.static-locations配置自定义静态文件的位置。

    web:
      upload-path: E:/data/
      
    spring:
      web:
        resources:
          static-locations: classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:${web.upload-path}
    
    • 配置web.upload-path为与项目代码分离的静态资源路径,即:文件上传保存根路径
    • 配置spring.web.resources.static-locations,除了带上Spring Boot默认的静态资源路径之外,加上file:${web.upload-path}指向外部的文件资源上传路径。该路径下的静态资源可以直接对外提供HTTP访问服务。

    1.2 文件上传的Controller实现

    @RestController
    public class FileUploadController {
    
        @Value("${web.upload-path}")
        private String uploadPath;
    
        @PostMapping("/upload")
        public String upload(MultipartFile uploadFile, HttpServletRequest request) throws IOException {
    
            // 在 uploadPath 文件夹中通过日期对上传的文件归类保存
            // 比如:/2019/06/06/cf13891e-4b95-4000-81eb-b6d70ae44930.png
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd/");
            String format = sdf.format(new Date());
            File folder = new File(uploadPath + format);
            if (!folder.isDirectory()) {
              folder.mkdirs();
            }
    
            // 对上传的文件重命名,避免文件重名
            String oldName = uploadFile.getOriginalFilename();
            String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
    
            // 文件保存到本地
            File destFile = new File(folder, newName);
            uploadFile.transferTo(destFile);
    
            // 返回上传文件的访问路径
            //https://localhost:8888/2020/10/18/a9a05df4-6615-4bb5-b859-a3f9bf4bfae0.jpg
            String filePath = request.getScheme() + "://" + request.getServerName()
                    + ":" + request.getServerPort() + "/"   + format + newName;
            return filePath;
        }
    }
    

    二.分布式文件系统MinIO

    2.1 MinIO简介

    MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
    MinIO官网:https://min.io

    2.2 Docker下安装MinIO

    1.创建目录
    一个用来存放配置,一个用来存储上传文件的目录。启动前需要先创建Minio外部挂载的配置文件(如:E:/minio/config),和存储上传文件的目录(如:E:/minio/data)

    2.拉取MinIO的Docker镜像:

    docker pull minio/minio
    

    3.创建Minio容器并运行

    docker run  -p 9000:9000 -p 9090:9090 --name minio -d --restart=always -e MINIO_ACCESS_KEY=minioadmin -e MINIO_SECRET_KEY=minioadmin -v E:/minio/data:/data -v E:/minio/config:/root/.minio minio/minio server /data  --console-address ":9090" --address ":9000"
    
    • 9000: S3-API端口,用于程序访问;
    • 9090:控制台端口,用户通过浏览器访问;
    • MINIO_ACCESS_KEY:指定 minio 默认的 access_key ,同时也是管理服务的登录用户名,这里指定的是minioadmin;
    • MINIO_SECRET_KEY:指定 minio 默认的 secret_key ,同时也是管理服务的的登录密码,这里指定的是minioadmin;

    4.访问MinIO控制台
    本机:http://127.0.0.1:9090或实际IP地址访问

    三.整合MinIO的JavaSDK

    3.1 整合MinIO

    1.pom.xml引入:

            <dependency>
                <groupId>io.minio</groupId>
                <artifactId>minio</artifactId>
                <version>8.4.5</version>
                <exclusions>
                    <exclusion>
                        <artifactId>okhttp</artifactId>
                        <groupId>com.squareup.okhttp3</groupId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>com.squareup.okhttp3</groupId>
                <artifactId>okhttp</artifactId>
                <version>4.9.0</version>
            </dependency>
    

    2.application.yml,服务信息要和我们安装的MinIO服务一致,这样才能正常连接测试:

    # MinIo文件服务器
    minio:
      endpoint: http://127.0.0.1:9000
      accessKey: minioadmin
      secretKey: minioadmin
    

    3.MinIoProperties.java 配置实体,将上文配置文件属性装载到实体配置对象中:

    @Data
    @ConfigurationProperties(prefix = "minio")
    public class MinIOProperties {
        private String endpoint;
        private String accessKey;
        private String secretKey;
    }
    

    4.MinIO提供了MinioClient对bucket、文件等进行操作,我们这里再封装一个工具类:

    @Component
    @Configuration
    @EnableConfigurationProperties({MinIOProperties.class})
    public class MinIOTemplate {
    
        private MinIOProperties minIo;
    
        public MinIOTemplate(MinIOProperties minIo) {
            this.minIo = minIo;
        }
    
        private MinioClient instance;
    
        //minio操作对象实例化
        @PostConstruct
        public void init() {
            instance = MinioClient.builder()
                     .endpoint(minIo.getEndpoint())
                     .credentials(minIo.getAccessKey(), minIo.getSecretKey())
                     .build();
        }
    
        /**
         * 判断 bucket是否存在
         */
        public boolean bucketExists(String bucketName)
                throws IOException, InvalidKeyException, InvalidResponseException,
                InsufficientDataException, NoSuchAlgorithmException,
                ServerException, InternalException, XmlParserException, ErrorResponseException {
    
                return instance.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        }
    
        /**
         * 创建 bucket
         */
        public void makeBucket(String bucketName) throws IOException, InvalidResponseException,
                InvalidKeyException, NoSuchAlgorithmException, ServerException,
                ErrorResponseException, XmlParserException,  InsufficientDataException, InternalException {
    
                boolean isExist = this.bucketExists(bucketName);
                if(!isExist) {
                    instance.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                }
        }
    
        /**
         * 文件上传
         * @param bucketName  bucket名称
         * @param objectName 对象名称,文件名称
         * @param filepath 文件路径
         */
        public ObjectWriteResponse putObject(String bucketName, String objectName, String filepath)
                throws IOException, InvalidKeyException, InvalidResponseException,
                InsufficientDataException, NoSuchAlgorithmException, ServerException,
                InternalException, XmlParserException, ErrorResponseException {
    
                return instance.uploadObject(
                             UploadObjectArgs.builder().bucket(bucketName).object(objectName).filename(filepath).build());
        }
    
        /**
         * 文件上传
         * @param bucketName bucket名称
         * @param objectName 对象名称,文件名称
         * @param inputStream 文件输入流
         */
        public ObjectWriteResponse putObject(String bucketName, String objectName, InputStream inputStream)
                throws IOException, InvalidKeyException, InvalidResponseException,
                InsufficientDataException, NoSuchAlgorithmException, ServerException,
                InternalException, XmlParserException, ErrorResponseException {
    
                return instance.putObject(
                             PutObjectArgs.builder()
                                     .bucket(bucketName)
                                     .object(objectName).stream(
                                     inputStream, -1, 10485760)
                                     .build());
        }
    
        /**
         * 删除文件
         * @param bucketName  bucket名称
         * @param objectName  对象名称
         */
        public void removeObject(String bucketName, String objectName)
                throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException,
                NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException {
    
                instance.removeObject(RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .build());
        }
    
        public String getObjectUrl(String bucketName, String objectName)
                throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException,
                NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException {
            return instance.getPresignedObjectUrl(
                            GetPresignedObjectUrlArgs.builder()
                                .method(Method.GET)
                                .bucket(bucketName)
                                .object(objectName)
    //                            .expiry(2, TimeUnit.HOURS)
                                .build());
        }
    }
    

    3.2 测试

    对1.2 文件上传的Controller实现进行改造,增加minio上传的代码:

    @RestController
    public class FileUploadController {
    
        @Resource
        private MinIOTemplate minIOTemplate;
    
        @Value("${web.upload-path}")
        private String uploadPath;
    
        @PostMapping("/upload")
        public String upload(MultipartFile uploadFile, HttpServletRequest request) throws IOException {
    
            // 在 uploadPath 文件夹中通过日期对上传的文件归类保存
            // 比如:/2019/06/06/cf13891e-4b95-4000-81eb-b6d70ae44930.png
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd/");
            String format = sdf.format(new Date());
            File folder = new File(uploadPath + format);
            if (!folder.isDirectory()) {
              folder.mkdirs();
            }
    
            // 对上传的文件重命名,避免文件重名
            String oldName = uploadFile.getOriginalFilename();
            String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
    
            // 文件保存到本地
            File destFile = new File(folder, newName);
            uploadFile.transferTo(destFile);
    
            //保存文件到minIO
            uploadToMinIO("test", format + newName, destFile.getAbsolutePath());
    
            // 返回上传文件的访问路径
            //https://localhost:8888/2020/10/18/a9a05df4-6615-4bb5-b859-a3f9bf4bfae0.jpg
            String filePath = request.getScheme() + "://" + request.getServerName()
                    + ":" + request.getServerPort() + "/"   + format + newName;
            return filePath;
        }
    
        private void uploadToMinIO(String bucketName, String objectName, String filePath) {
            try {
                minIOTemplate.makeBucket(bucketName);
                ObjectWriteResponse response = minIOTemplate.putObject(bucketName, objectName, filePath);
                System.out.println(response.object());
    
                //获取文件访问路径
                String url = minIOTemplate.getObjectUrl("test", objectName);
                System.out.println(url);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

    注意:MinIO默认获取到的文件范围URL有效期为7天,可以通过在控制台bucket里面进行规制配置,实现永久访问。

    相关文章

      网友评论

          本文标题:SpringBoot整合分布式文件系统

          本文链接:https://www.haomeiwen.com/subject/spfpudtx.html