Skip to content

OpenDAL的简单使用(Java 向)

约 827 字大约 3 分钟

2025-08-26

OpenDAL是由阿帕奇基金会孵化的数据访问层,从官网介绍中可知其目的是为了为各种语言提供一种访问各种数据平台如文件系统、对象存储、缓存等一系列存储的统一接口 image 截止至其 releasev0.54.0版本已支持 Go、Java、Node.js、Python

简单实现

本文将以 Java 为例,在 Spring 框架中进行对 S3 的简单实现 参考Java | Apache OpenDAL™ --- Java | Apache OpenDAL™

Maven 配置

因为OpenDAL的底层使用 rust 实现,因此在 java 中需要通过调用 native 方法 因此需要os-maven-plugin自动检测分类器

<build>
<extensions>
  <extension>
    <groupId>kr.motd.maven</groupId>
    <artifactId>os-maven-plugin</artifactId>
    <version>1.7.0</version>
  </extension>
</extensions>
</build>

需要引用两个依赖,第一个是opendal的 JIN 接口,第二个是与平台相关的 Native 库,两者缺一不可

os.detected.classifier由上面的插件提供,无需手动设置

<dependencies>
  <dependency>
    <groupId>org.apache.opendal</groupId>
    <artifactId>opendal</artifactId>
    <version>${opendal.version}</version>
  </dependency>
  <dependency>
    <groupId>org.apache.opendal</groupId>
    <artifactId>opendal</artifactId>
    <version>${opendal.version}</version>
    <classifier>${os.detected.classifier}</classifier>
  </dependency>
</dependencies>

配置后端存储

OpenDAL提供了操作类OperatorAsyncOperator封装了 write、read、remove等文件操作,这也就意味着想要使用这一抽象层操作存储后端只需要实例化该类

Operator类为例,它提供了两种静态工厂方法用于获取实例,两者本质并无区别,最终目的都是传入你的后端存储配置

public static Operator of(ServiceConfig config)
public static Operator of(String scheme, Map<String, String> map)

ServiceConfig

一个配置接口类,所有适配的后端类型(如S3OSS等具体的存储提供方)的配置类都实现该接口,使用第一种方法也就是需要自行构建一个ServiceConfig

Operator operator = Operator.of(ServiceConfig.S3.builder()  
        .accessKeyId("")  
        .secretAccessKey("")  
        .bucket("")  
        .build());

动态加载配置

手动构建ServiceConfig较为繁琐,在 Spring 中可以考虑使用配置属性绑定功能从Application.yml中动态加载配置,更具泛用性

@Configuration  
public class OpenDALConfig {  
    @Bean  
    @ConfigurationProperties("opendal")  
    public OpenDALProperties openDALProperties() {  
        return new OpenDALProperties();  
    }  
  
    @Bean  
    public Operator operator(OpenDALProperties properties) {  
        if (properties.getScheme() == null || properties.getScheme().trim().isEmpty()) {  
            throw new IllegalArgumentException("opendal.scheme is required");  
        }  
        return Operator.of(properties.getScheme(), properties.getConfig());  
    }  
  
    @Getter  
    @Setter    
    public static class OpenDALProperties {  
        private String scheme; // 访问的数据存储类型  
        private Map<String, String> config = new HashMap<>();  
    }  
}

当更换服务提供商时只需要更新 .yml 配置文件即可,无需变动代码

:填写的config字段需要与服务接口在ServiceConfig中的成员变量名一致,服务名与scheme()中返回的值一致,如obs

勘误:需要与对应ServiceConfig实现中的configMap()方法一致,因为这才是 native 方法中真正接收的参数名

Application.yml
# 其他配置...

opendal:  
  config:  
    root: /
    endpoint: obs.cn-south-1.myhuaweicloud.com
    access_key_id: ak
    secret_access_key: sk
    bucket: bucket-name
    enable_versioning: false  
  scheme: obs

对应

public static class Obs implements ServiceConfig {  
    public final String root;  
	public final String endpoint;  
	public final String accessKeyId;  
	public final String secretAccessKey;  
	public final String bucket;  
	public final Boolean enableVersioning;  
	  
	public String scheme() {  
	    return "obs";  
	}  
	  
	public Map<String, String> configMap() {  
	    HashMap<String, String> map = new HashMap();  
	    if (this.root != null) {  
	        map.put("root", this.root);  
	    }  
	  
	    if (this.endpoint != null) {  
	        map.put("endpoint", this.endpoint);  
	    }  
	  
	    if (this.accessKeyId != null) {  
	        map.put("access_key_id", this.accessKeyId);  
	    }  
	  
	    if (this.secretAccessKey != null) {  
	        map.put("secret_access_key", this.secretAccessKey);  
	    }  
	  
	    if (this.bucket != null) {  
	        map.put("bucket", this.bucket);  
	    }  
	  
	    if (this.enableVersioning != null) {  
	        map.put("enable_versioning", String.valueOf(this.enableVersioning));  
	    }  
	  
	    return map;  
	}
    
}

简单使用

读取bucket-name:/file.txt

@SpringBootTest  
public class opendalTest {  
    @Autowired  
    Operator operator;  
    @Test  
    void readTest() {  
        byte[] read = operator.read("file.txt");  
        System.out.println(Arrays.toString(read));  
    }  
}
// 输出
[97, 98, 99]