SpringBoot × MinIO 极速开发指南:对象存储服务高可用实战

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储OSS,敏感数据保护2.0 200GB 1年
对象存储 OSS,内容安全 1000 次 1年
简介: 生成临时访问URL接口和文件预览其实是同一个方法,只是文件预览内定了七天访问,而这个方法可以自行制定,单位是秒。方法,所以返回的是地址信息,但是可以通过dubug看到Bucket中的属性,确实是当前所有桶信息。配置类中奖MinIOClient客户端注入到Springboot中。MinIO集群的在Linux上的部署可以参考:​​​​​​​。Nginx代理MinIO集群可以参考:​​​​​​​。从Bucket源码可以看出,并没有实现。

 目录

SpringBoot Integrate MinIO

1. MinIO 安装部署

2. SpringBoot 项目配置

2.1. 添加依赖

2.2. 配置文件

2.3. 桶的数量配置

2.4. 配置类

3. MinIO 核心功能实现

3.1. MinIO 工具类

3.2. MinIO 控制器

4. 功能测试验证

4.1. 程序启动

4.2. 文件上传接口验证

4.3. 文件下载接口验证

4.4. 文件预览(7天有效期)接口验证

4.5. 文件删除接口验证

4.6. 获取文件列表接口验证

4.7. 生成临时访问URL接口验证

4.8. 获取永久访问URL接口验证

4.9. 创建存储桶接口验证

4.10. 获取所有存储桶接口验证

4.11. 删除存储桶接口验证


SpringBoot Integrate MinIO

本章的知识网络:

image.gif 编辑

1. MinIO 安装部署

MinIO的在Linux上的部署可以参考:MinIO在Linux上的安装与部署_minio linux部署-CSDN博客

MinIO集群的在Linux上的部署可以参考:MinIO在Linux上的集群安装与部署-CSDN博客

Nginx代理MinIO集群可以参考:Nginx代理MinIO集群-CSDN博客

2. SpringBoot 项目配置

2.1. 添加依赖

添加相应的pom.xml文件依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-ui</artifactId>
            <version>1.6.14</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.5.6</version>
            <exclusions>
                <exclusion>
                    <groupId>com.squareup.okhttp3</groupId>
                    <artifactId>okhttp</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.11.0</version> <!-- 使用最新稳定版 -->
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.30</version>
        </dependency>

image.gif

2.2. 配置文件

配置application.yml文件

minio:
  endpoint: http://192.168.33.205:9000 # Nginx 负载均衡器的地址
  access-key: gVqpDMoCHridNBp9wI1O # 访问密钥
  secret-key: H6E19mqETNxYFJ6PgSK1V6tgdWjeiqt8e6BMnkuV # 安全密钥
  bucketName: springboot-mino-test-bucket # 存储桶名称
# 文件上传大小限制
spring:
  servlet:
    multipart:
      enabled: true
      max-file-size: 100MB
      max-request-size: 100MB

image.gif

2.3. 桶的数量配置

在项目开发中,使用 MinIO 桶的数量取决于具体的业务需求和技术架构设计,以下是不同场景的实践建议:

  1. 小型项目或简单业务在配置文件中配置一个桶即可,简单高效,维护成本低,但是灵活性差。
  2. 中大型项目或复杂业务需要权限控制、数据隔离和多租户情况时,可以再配置文件中预设高频使用的桶(例如用户头像、日志文件等),在接口中动态制定低频使用的桶(租户个人空间)

以下是部分示例:

minio:
  buckets:
    user: app-user-data
    product: app-product-images
    log: app-system-logs

image.gif

minio:
  buckets:
    dev: app-dev-bucket
    test: app-test-bucket
    prod: app-prod-bucket

image.gif

2.4. 配置类

配置类中奖MinIOClient客户端注入到Springboot中

@Configuration
@Getter
public class MinioConfig {
    @Value("${minio.endpoint}")
    private String endpoint;
    @Value("${minio.access-key}")
    private String accessKey;
    @Value("${minio.secret-key}")
    private String secretKey;
    @Value("${minio.bucketName}")
    private String bucketName;
    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
        .endpoint(endpoint)
        .credentials(accessKey, secretKey)
        .build();
    }
}

image.gif

3. MinIO 核心功能实现

3.1. MinIO 工具类

MinIO工具类中包含了操作MinIO服务的核心方法:

  • 文件上传(支持自动重命名)
  • 文件下载(文件输入流和浏览器直接下载)
  • 文件删除
  • 获取桶中文件列表
  • 存储桶管理(增、删、是否存在、获取桶列表)
  • 生成临时访问链接
  • 图片预览
/**
 * Minio对象存储操作工具类
 * 包含存储桶管理、文件操作、浏览器下载等完整功能
 */
@Component
public class MinioUtil {
    private final MinioClient minioClient;
    private final MinioConfig minioConfig;
    @Autowired
    public MinioUtil(MinioClient minioClient, MinioConfig minioConfig) {
        this.minioClient = minioClient;
        this.minioConfig = minioConfig;
    }
    /* 存储桶操作系列方法 */
    /**
     * 检查存储桶是否存在
     *
     * @param bucketName 存储桶名称
     * @return 是否存在
     * @throws Exception Minio操作异常
     */
    public boolean bucketExists(String bucketName) throws Exception {
        return minioClient.bucketExists(BucketExistsArgs.builder()
                .bucket(bucketName)
                .build());
    }
    /**
     * 创建新存储桶
     *
     * @param bucketName 存储桶名称
     * @return 是否创建成功
     * @throws Exception Minio操作异常
     */
    public boolean createBucket(String bucketName) throws Exception {
        if (!bucketExists(bucketName)) {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
            return true;
        }
        return false;
    }
    /**
     * 删除存储桶(存储桶必须为空)
     *
     * @param bucketName 存储桶名称
     * @throws Exception Minio操作异常
     */
    public void removeBucket(String bucketName) throws Exception {
        minioClient.removeBucket(RemoveBucketArgs.builder()
                .bucket(bucketName)
                .build());
    }
    /**
     * 获取全部存储桶列表
     *
     * @return 存储桶信息列表
     * @throws Exception Minio操作异常
     */
    public List<Bucket> listAllBuckets() throws Exception {
        return minioClient.listBuckets();
    }
    /* 文件操作系列方法 */
    /**
     * 上传文件到默认存储桶
     *
     * @param file       上传的文件对象
     * @param objectName 存储对象名称(包含路径)
     * @return 文件访问URL
     * @throws Exception Minio操作异常
     */
    public String uploadFile(MultipartFile file, String objectName) throws Exception {
        // 自动生成唯一文件名
        if (objectName == null || objectName.isEmpty()) {
            objectName = generateUniqueName(file.getOriginalFilename());
        }
        minioClient.putObject(PutObjectArgs.builder()
                .bucket(minioConfig.getBucketName())
                .object(objectName)
                .stream(file.getInputStream(), file.getSize(), -1)
                .contentType(file.getContentType())
                .build());
        return getFileUrl(objectName);
    }
    /**
     * 获取文件临时访问URL
     *
     * @param objectName 存储对象名称
     * @param expiry     有效期(单位:秒)
     * @return 临时访问URL
     * @throws Exception Minio操作异常
     */
    public String getPresignedUrl(String objectName, int expiry) throws Exception {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(minioConfig.getBucketName())
                        .object(objectName)
                        .expiry(expiry, TimeUnit.SECONDS)
                        .build());
    }
    /**
     * 图片预览(生成7天有效的URL)
     *
     * @param objectName 存储对象名称
     * @return 预览URL
     * @throws Exception Minio操作异常
     */
    public String previewImage(String objectName) throws Exception {
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(minioConfig.getBucketName())
                        .object(objectName)
                        .expiry(7, TimeUnit.DAYS)
                        .build());
    }
    /**
     * 获取文件输入流
     *
     * @param objectName 存储对象名称
     * @return 文件流
     * @throws Exception Minio操作异常
     */
    public InputStream downloadFile(String objectName) throws Exception {
        return minioClient.getObject(GetObjectArgs.builder()
                .bucket(minioConfig.getBucketName())
                .object(objectName)
                .build());
    }
    /**
     * 直接下载文件到HttpServletResponse(浏览器下载)
     *
     * @param objectName 存储对象名称
     * @param response   HttpServletResponse
     * @throws Exception Minio操作异常或IO异常
     */
    public void downloadToResponse(String objectName, HttpServletResponse response) throws Exception {
        // 获取文件元数据
        StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder()
                .bucket(minioConfig.getBucketName())
                .object(objectName)
                .build());
        // 设置响应头
        response.setContentType(stat.contentType());
        response.setHeader("Content-Disposition",
                "attachment; filename=\"" + URLEncoder.encode(objectName, StandardCharsets.UTF_8.name()) + "\"");
        response.setContentLengthLong(stat.size());
        // 流式传输文件内容
        try (InputStream is = downloadFile(objectName);
             OutputStream os = response.getOutputStream()) {
            IOUtils.copy(is, os);
            os.flush();
        }
    }
    /**
     * 删除文件
     *
     * @param objectName 存储对象名称
     * @throws Exception Minio操作异常
     */
    public void deleteFile(String objectName) throws Exception {
        minioClient.removeObject(RemoveObjectArgs.builder()
                .bucket(minioConfig.getBucketName())
                .object(objectName)
                .build());
    }
    /**
     * 列出存储桶中的所有文件
     *
     * @param bucketName 存储桶名称
     * @return 文件信息列表
     * @throws Exception Minio操作异常
     */
    public List<String> listAllFiles(String bucketName) throws Exception {
        List<String> list = new ArrayList<>();
        for (Result<Item> result : minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build())) {
            list.add(result.get().objectName());
        }
        return list;
    }
    /**
     * 获取文件元数据
     *
     * @param objectName 存储对象名称
     * @return 文件元数据
     * @throws Exception Minio操作异常
     */
    public StatObjectResponse getObjectStat(String objectName) throws Exception {
        return minioClient.statObject(StatObjectArgs.builder()
                .bucket(minioConfig.getBucketName())
                .object(objectName)
                .build());
    }
    /**
     * 生成永久访问URL(需要存储桶设置为公开)
     *
     * @param objectName 存储对象名称
     * @return 直接访问URL
     */
    public String getPermanentUrl(String objectName) {
        return String.format("%s/%s/%s",
                minioConfig.getEndpoint(),
                minioConfig.getBucketName(),
                objectName);
    }
    /**
     * 验证文件类型白名单
     *
     * @param file         上传文件
     * @param allowedTypes 允许的类型列表
     * @throws IllegalArgumentException 文件类型不合法
     */
    public void validateFileType(MultipartFile file, List<String> allowedTypes) {
        String fileType = file.getContentType();
        if (!allowedTypes.contains(fileType)) {
            throw new IllegalArgumentException("不支持的文件类型: " + fileType);
        }
    }
    /* 辅助方法 */
    private String generateUniqueName(String originalFileName) {
        return UUID.randomUUID().toString().replace("-", "")
                + "_" + originalFileName;
    }
    private String getFileUrl(String objectName) throws Exception {
        return minioConfig.getEndpoint() + "/"
                + minioConfig.getBucketName() + "/" + objectName;
    }
    /* 扩展功能方法 */
    /**
     * 复制文件到新位置
     *
     * @param sourceBucket 源存储桶
     * @param sourceObject 源文件
     * @param destBucket   目标存储桶
     * @param destObject   目标文件
     * @throws Exception Minio操作异常
     */
    public void copyObject(String sourceBucket, String sourceObject,
                           String destBucket, String destObject) throws Exception {
        minioClient.copyObject(CopyObjectArgs.builder()
                .source(CopySource.builder()
                        .bucket(sourceBucket)
                        .object(sourceObject)
                        .build())
                .bucket(destBucket)
                .object(destObject)
                .build());
    }
}

image.gif

3.2. MinIO 控制器

FileController在MinIO工具基础上实现了以下接口:

image.gif 编辑

@Tag(name = "文件管理接口")
@RestController
@RequestMapping("/minio")
public class MinioController {
    private final MinioUtil minioUtil;
    private final MinioConfig minioConfig;
    @Autowired
    public MinioController(MinioUtil minioUtil, MinioConfig minioConfig) {
        this.minioUtil = minioUtil;
        this.minioConfig = minioConfig;
    }
    @Operation(summary = "文件上传")
    @PostMapping("/upload")
    public R<String> uploadFile(@RequestParam("file") MultipartFile file,
                                @RequestParam(value = "objectName", required = false) String objectName) {
        try {
            if (file.isEmpty()) {
                return R.error(HttpStatus.BAD_REQUEST.value(), "上传文件不能为空");
            }
            String url = minioUtil.uploadFile(file, objectName);
            return R.success("上传成功", url);
        } catch (Exception e) {
            return R.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
        }
    }
    @Operation(summary ="文件下载")
    @GetMapping("/download/{objectName}")
    public void downloadFile(@PathVariable String objectName, HttpServletResponse response) {
        try {
            minioUtil.downloadToResponse(objectName, response);
        } catch (Exception e) {
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        }
    }
    @Operation(summary ="文件预览(7天有效期)")
    @GetMapping("/preview/{objectName}")
    public R<String> previewFile(@PathVariable String objectName) {
        try {
            String url = minioUtil.previewImage(objectName);
            return R.success("获取预览地址成功", url);
        } catch (Exception e) {
            return R.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
        }
    }
    @Operation(summary ="删除文件")
    @DeleteMapping("/delete/{objectName}")
    public R<Void> deleteFile(@PathVariable String objectName) {
        try {
            minioUtil.deleteFile(objectName);
            return R.success("删除成功");
        } catch (Exception e) {
            return R.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
        }
    }
    @Operation(summary ="获取文件列表")
    @GetMapping("/list")
    public R<List<String>> listFiles(@RequestParam(required = false) String bucketName) {
        try {
            String targetBucket = (bucketName != null && !bucketName.isEmpty())
                    ? bucketName : minioConfig.getBucketName();
            List<String> files = minioUtil.listAllFiles(targetBucket);
            return R.success("获取成功", files);
        } catch (Exception e) {
            return R.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
        }
    }
    @Operation(summary ="生成临时访问URL")
    @GetMapping("/presigned-url")
    public R<String> getPresignedUrl(@RequestParam String objectName,
                                    @RequestParam(defaultValue = "3600") int expiry) {
        try {
            String url = minioUtil.getPresignedUrl(objectName, expiry);
            return R.success("生成成功", url);
        } catch (Exception e) {
            return R.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
        }
    }
    @Operation(summary ="获取永久访问URL")
    @GetMapping("/permanent-url/{objectName}")
    public R<String> getPermanentUrl(@PathVariable String objectName) {
        try {
            String url = minioUtil.getPermanentUrl(objectName);
            return R.success("获取成功", url);
        } catch (Exception e) {
            return R.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
        }
    }
    // 存储桶管理相关接口
    @Operation(summary ="创建存储桶")
    @PostMapping("/buckets/{bucketName}")
    public R<Void> createBucket(@PathVariable String bucketName) {
        try {
            boolean created = minioUtil.createBucket(bucketName);
            return created ? R.success("创建成功") : R.error(HttpStatus.INTERNAL_SERVER_ERROR.value(),"存储桶已存在");
        } catch (Exception e) {
            return R.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
        }
    }
    @Operation(summary ="删除存储桶")
    @DeleteMapping("/buckets/{bucketName}")
    public R<Void> deleteBucket(@PathVariable String bucketName) {
        try {
            minioUtil.removeBucket(bucketName);
            return R.success("删除成功");
        } catch (Exception e) {
            return R.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
        }
    }
    @Operation(summary ="获取所有存储桶")
    @GetMapping("/buckets")
    public R<List<io.minio.messages.Bucket>> listBuckets() {
        try {
            List<io.minio.messages.Bucket> buckets = minioUtil.listAllBuckets();
            return R.success("获取成功", buckets);
        } catch (Exception e) {
            return R.error(HttpStatus.INTERNAL_SERVER_ERROR.value(), e.getMessage());
        }
    }
}

image.gif

4. 功能测试验证

下方提供了功能测试的图例,我将apipost的地址共享在这里,有需要测试的朋友可以自行测试:

Apipost-基于协作, 不止于API文档、调试、Mock、自动化测试

4.1. 程序启动

访问地址是127.0.0.1:8080

image.gif 编辑

4.2. 文件上传接口验证

ApiPost验证文件上传:

image.gif 编辑

验证文件确实上传成功:

image.gif 编辑

image.gif 编辑

验证返回的文件url:

image.gif 编辑

4.3. 文件下载接口验证

ApiPost验证文件下载:

image.gif 编辑

4.4. 文件预览(7天有效期)接口验证

ApiPost验证文件预览(7天有效期):

image.gif 编辑

验证文件预览URL:

image.gif 编辑

4.5. 文件删除接口验证

准备删除的文件

image.gif 编辑

ApiPost验证文件删除:

image.gif 编辑

准备删除的文件已经删除了

image.gif 编辑

4.6. 获取文件列表接口验证

ApiPost验证获取文件列表:

image.gif 编辑

4.7. 生成临时访问URL接口验证

生成临时访问URL接口和文件预览其实是同一个方法,只是文件预览内定了七天访问,而这个方法可以自行制定,单位是秒

ApiPost验证生成临时URL:

image.gif 编辑

验证临时访问URL

image.gif 编辑

4.8. 获取永久访问URL接口验证

ApiPost验证获取永久访问URL:

image.gif 编辑

验证永久访问URL:

image.gif 编辑

4.9. 创建存储桶接口验证

ApiPost验证创建存储桶:

image.gif 编辑

验证桶确实创建成功:

image.gif 编辑

再次调用返回桶已存在

image.gif 编辑

4.10. 获取所有存储桶接口验证

ApiPost验证获取所有存储桶:

image.gif 编辑

从Bucket源码可以看出,并没有实现toString()方法,所以返回的是地址信息,但是可以通过dubug看到Bucket中的属性,确实是当前所有桶信息

image.gif 编辑

image.gif 编辑

4.11. 删除存储桶接口验证

ApiPost验证删除存储桶:

image.gif 编辑

桶已删除

image.gif 编辑

再次调用返回该桶已不存在

image.gif 编辑


相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
目录
相关文章
|
24天前
|
编解码 网络协议 算法
SpringBoot × TCP 极速开发指南:工业级TCP通信协议栈操作手册
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程的SpringBoot × TCP 极速开发指南,废话不多说直接开始~
57 0
|
24天前
|
存储 Linux 网络安全
Linux单节点单硬盘部署MinIO:安装部署MinIO对象存储服务实战指南
MinIO 要求用于存储数据的驱动器必须是独立的、非系统分区,以确保数据的安全性和性能。如果数据目录位于系统根分区,MinIO 会拒绝使用它,并报错。因此我们需要一个新的磁盘来作为MinIO的数据目录挂载点。ext4 是 Linux 系统中广泛使用的一种文件系统,它具备高可靠性、高性能以及支持大文件和大分区等优点。这里使用的是CentOS7版本,使用的是默认下载器yum,如果是CentOS8版本,则使用dnf下载器。挂载/mnt/data到新分区,查看挂载情况。获取新分区/dev/sdb1的UUID。
660 57
|
3月前
|
存储 Java 文件存储
🗄️Spring Boot 3 整合 MinIO 实现分布式文件存储
本文介绍了如何基于Spring Boot 3和MinIO实现分布式文件存储。随着应用规模扩大,传统的单机文件存储方案难以应对大规模数据和高并发访问,分布式文件存储系统成为更好的选择。文章详细讲解了MinIO的安装、配置及与Spring Boot的整合步骤,包括Docker部署、MinIO控制台操作、Spring Boot项目中的依赖引入、配置类编写及工具类封装等内容。最后通过一个上传头像的接口示例展示了具体的开发和测试过程,强调了将API操作封装成通用工具类以提高代码复用性和可维护性的重要性。
553 7
🗄️Spring Boot 3 整合 MinIO 实现分布式文件存储
|
5月前
|
存储 Java 文件存储
Spring Boot 3 整合 Minio 实现文件存储
本文介绍了如何使用 Spring Boot 3 整合 MinIO 实现文件存储服务。MinIO 是一款高性能的对象存储服务器,适合大规模数据存储与分析,支持多种部署环境且文档完备、开源免费。从 MinIO 的快速安装、配置文件公开访问,到 Spring Boot 中集成 MinIO 客户端的步骤,包括创建用户访问密钥、引入依赖包、添加配置信息、编写 MinIO 客户端配置类及上传和预览文件的服务代码。最后通过 Apifox 进行文件上传测试,并验证文件是否成功存储及预览功能是否正常。关注公众号“Harry技术”,回复 minio 获取源码地址。
386 76
|
6月前
|
XML Java API
Spring Boot集成MinIO
本文介绍了如何在Spring Boot项目中集成MinIO,一个高性能的分布式对象存储服务。主要步骤包括:引入MinIO依赖、配置MinIO属性、创建MinIO配置类和服务类、使用服务类实现文件上传和下载功能,以及运行应用进行测试。通过这些步骤,可以轻松地在项目中使用MinIO的对象存储功能。
433 5
|
7月前
|
存储 Java API
开源对象存储服务(MinIO),正在备受欢迎!
本文介绍了MinIO,一个高性能、开源的对象存储服务器,兼容Amazon S3 API,适合存储大量非结构化数据。文章详细讲解了MinIO在Java中的使用方法,包括添加依赖、初始化客户端、基本操作(创建桶、上传/下载/删除对象)和高级功能(设置桶策略、使用预签名URL),并提供了Spring Boot集成MinIO的示例。
677 4
|
4月前
|
SQL 分布式计算 Serverless
基于阿里云 EMR Serverless Spark 版快速搭建OSS日志分析应用
基于阿里云 EMR Serverless Spark 版快速搭建OSS日志分析应用
|
11月前
|
机器学习/深度学习 人工智能 专有云
人工智能平台PAI使用问题之怎么将DLC的数据写入到另一个阿里云主账号的OSS中
阿里云人工智能平台PAI是一个功能强大、易于使用的AI开发平台,旨在降低AI开发门槛,加速创新,助力企业和开发者高效构建、部署和管理人工智能应用。其中包含了一系列相互协同的产品与服务,共同构成一个完整的人工智能开发与应用生态系统。以下是对PAI产品使用合集的概述,涵盖数据处理、模型开发、训练加速、模型部署及管理等多个环节。
|
7月前
|
分布式计算 Java 开发工具
阿里云MaxCompute-XGBoost on Spark 极限梯度提升算法的分布式训练与模型持久化oss的实现与代码浅析
本文介绍了XGBoost在MaxCompute+OSS架构下模型持久化遇到的问题及其解决方案。首先简要介绍了XGBoost的特点和应用场景,随后详细描述了客户在将XGBoost on Spark任务从HDFS迁移到OSS时遇到的异常情况。通过分析异常堆栈和源代码,发现使用的`nativeBooster.saveModel`方法不支持OSS路径,而使用`write.overwrite().save`方法则能成功保存模型。最后提供了完整的Scala代码示例、Maven配置和提交命令,帮助用户顺利迁移模型存储路径。
|
10月前
|
存储 机器学习/深度学习 弹性计算
阿里云EMR数据湖文件系统问题之OSS-HDFS全托管服务的问题如何解决
阿里云EMR数据湖文件系统问题之OSS-HDFS全托管服务的问题如何解决
130 1