数据存储后端【免费下载链接】opendalApache OpenDAL: One Layer, All Storage.项目地址https://gitcode.com/gh_mirrors/op/opendal点击查看免费下载Apache OpenDALOne Layer, All Storage的 C Binding 让 C 开发者通过同一套 API 访问 S3、GCS、Azure Blob、HDFS、本地文件系统等 50 存储服务底层由 Rust 核心驱动。本指南以官方 Overview 文档为骨架系统讲解该 Binding 的实验性状态、CMake 集成方式、CMake 选项含义并结合仓库源码与示例带你从零构建绑定、编写第一个读写程序进而掌握流式读取、目录遍历、能力内省与异步 API 的完整用法。Binding 定位与现状Apache OpenDAL C Binding 是一个experimental / work in progressWIP的绑定层其定位与状态如下未正式发布目前没有公开发布的包只能从源码通过 CMake 构建API 不稳定接口可能在版本之间无通知地变化暂不建议用于生产环境编译器支持有限目前仅支持Clang和AppleClangMSVC 与 GCC 尚未测试版本节奏独立C Binding 独立于 Rustopendalcrate 发版追踪兼容性时应以 C Binding 自身的版本为准见 bindings/cpp/README.md。该 Binding 的 C 头文件与实现位于 bindings/cppRust FFI 桥接代码在同目录下的src/*.rs中。Cargo 侧将 crate 编译为静态库crate-type [staticlib]再通过 cxx 桥接暴露给 C 调用见 bindings/cpp/Cargo.toml。如果你是第一次接触 OpenDAL 的抽象模型建议先阅读 Concepts共享存储语义、路径与操作概念因为每个绑定都是用自己的原生 API 表达同一套语义。核心能力总览官方 Overview 文档概括了 C Binding 的五大核心能力仓库头文件进一步印证了这些接口同步 APIopendal.hpp以opendal::Operator为入口提供读、写、stat、list、copy、rename、remove 与目录创建等操作。完整的公开方法签名见 bindings/cpp/include/opendal.hpp 中的Operator类opendal.hpp#L51-L211操作方法说明读取Read(path)/Read(path, options)一次性读入std::string会有一次额外拷贝大对象建议改用GetReader写入Write(path, data)/Write(path, data, options)以std::string_view写入流式读写GetReader(path)/GetWriter(path)返回Reader/Writer避免中间拷贝存在性Exists(path)旧接口IsExist已标记 deprecated判断路径是否存在目录CreateDir(path)创建目录拷贝/重命名Copy(src, dst)/Rename(src, dst)支持带 options 的重载删除Remove(path)/RemoveAll(paths)删除单个或批量删除元数据Stat(path)返回Metadata列表List(path)/GetLister(path)一次性返回全部条目或返回惰性迭代器能力内省Info()返回Capability结构体异步 APIopendal_async.hpp异步 API 是独立、可选的头文件通过-DOPENDAL_ENABLE_ASYNCON开启要求 C20。opendal::async::Operator暴露与同步 API 相同的操作但以 Rust future 的形式返回见 bindings/cpp/include/opendal_async.hpp。流式读取Reader/ReaderStream用于大对象读取而无需中间拷贝ReaderStream将Reader包装为标准std::istream内部带 8192 字节缓冲见 opendal.hpp#L279-L366。Lister/Lister::Iterator则用于目录遍历。能力内省Operator::Info()返回Capability结构体允许在运行时检查后端支持哪些特性如是否支持stat、read、write、copy、rename、list、presign以及各种条件选项字段定义见 bindings/cpp/include/data_structure.hpp#L339-L375。服务按需编译服务由构建期 CMake 变量OPENDAL_FEATURES控制例如opendal/services-s3,opendal/services-memory。这些 feature 会原样透传给 Rust 侧cargo build --features ...见 CMakeLists.txt#L143-L145从而控制静态库中实际编译进哪些存储服务。通过 CMake 集成到你的项目官方文档给出了两种推荐集成方式均以仓库根目录下的 bindings/cpp 作为 CMake 子项目。方式一FetchContent推荐include(FetchContent) FetchContent_Declare( opendal-cpp GIT_REPOSITORY https://gitcode.com/gh_mirrors/op/opendal.git GIT_TAG v0.40.0 SOURCE_SUBDIR bindings/cpp ) FetchContent_MakeAvailable(opendal-cpp) target_link_libraries(your_target opendal_cpp)SOURCE_SUBDIR bindings/cpp告诉 CMake 只将该仓库下的 C Binding 子目录纳入构建链接目标名称为opendal_cpp静态库实际由 CMakeLists.txt#L155 的add_library(opendal_cpp STATIC ...)生成。方式二Vendored source源码内嵌先将仓库克隆到第三方目录并检出指定 tagmkdir third_party cd third_party git clone https://gitcode.com/gh_mirrors/op/opendal.git cd opendal git checkout v0.40.0然后在你的 CMakeLists 中直接添加子目录add_subdirectory(third_party/opendal/bindings/cpp) target_link_libraries(your_target opendal_cpp)构建前提条件CMake ≥ 3.22与 CMakeLists.txt#L18 的cmake_minimum_required(VERSION 3.22)一致Clang 或 AppleClang支持 C17启用异步 API 时要求 C20Rust 工具链Binding 在构建过程中会把 Rust 核心一并编译CMake 内部通过自定义目标cargo_build调用cargo build --manifest-path ...见 CMakeLists.txt#L147-L153。从源码构建 Binding进入 Binding 目录后执行标准 CMake 流程该步骤与 02-getting-started.md 一致cd bindings/cpp mkdir build cd build cmake -DCMAKE_C_COMPILERclang -DCMAKE_CXX_COMPILERclang .. make面向开发环境时会一并启用测试与文档生成cmake -DCMAKE_C_COMPILERclang -DCMAKE_CXX_COMPILERclang \ -DOPENDAL_DEVON .. makeCMake 选项速查表来自 bindings/cpp/README.md 并对照 CMakeLists.txt#L37-L43选项默认值说明OPENDAL_DEVOFF一键开启所有开发选项地址消毒器、文档、测试OPENDAL_FEATURES逗号分隔的服务列表如opendal/services-s3OPENDAL_ENABLE_ASYNCOFF启用异步 API要求 C20OPENDAL_ENABLE_TESTINGOFF构建并启用测试OPENDAL_ENABLE_DOCUMENTATIONOFF生成 Doxygen 文档OPENDAL_ENABLE_ADDRESS_SANITIZEROFF启用 Address Sanitizer几点源码层面的补充说明开启OPENDAL_ENABLE_ASYNC后CMake 会把 C 标准强制设为 C20且若非 Clang/AppleClang 会直接FATAL_ERRORCMakeLists.txt#L45-L53开启测试后CMake 会自动向OPENDAL_FEATURES追加testingfeatureCMakeLists.txt#L62-L68OPENDAL_ENABLE_ASYNC同样会追加asyncfeature并额外编译async.rs.cc桥接代码CMakeLists.txt#L135-L141OPENDAL_DEVON等价于同时打开 ASan、文档与测试三个开关CMakeLists.txt#L56-L60。第一个程序内存服务读写官方给出的入门示例运行在memory服务上无需任何凭证覆盖写入、读取、元数据检查与删除。完整可运行代码见 examples/cpp/getting_started.cppquickstart区域核心内容如下#include opendal.hpp #include iostream #include string int main() { // Construct an operator for the memory service (no credentials needed). opendal::Operator op(memory); // Write a string. op.Write(hello.txt, Hello, OpenDAL!); // Read it back. std::string data op.Read(hello.txt); std::cout data \n; // Hello, OpenDAL! // Inspect metadata. opendal::Metadata meta op.Stat(hello.txt); std::cout size meta.ContentLength() \n; std::cout is_file meta.IsFile() \n; // Remove. op.Remove(hello.txt); std::cout exists op.Exists(hello.txt) \n; // 0 return 0; }编译并运行假设 Binding 已构建到你的构建树中clang -stdc17 -I/path/to/opendal/bindings/cpp/include \ -L/path/to/build -lopendal_cpp \ hello.cpp -o hello ./hello从源码结构看Operator构造函数的第二个参数是一个std::unordered_mapstd::string, std::string配置表默认为空第三个参数是可选的 layer 列表见 opendal.hpp#L62-L64。memory服务不要求任何配置项因此只传 scheme 即可。连接真实后端以 S3 为例切换真实后端时只有 scheme 和配置表发生变化所有操作完全一致。后端参数以std::unordered_mapstd::string, std::string传入#include opendal.hpp #include iostream int main() { opendal::Operator op(s3, { {bucket, my-bucket}, {region, us-east-1}, }); op.Write(hello.txt, Hello from S3!); std::string data op.Read(hello.txt); std::cout data \n; }配置键与各服务的官方文档Services Configuration保持一致仓库侧可参考 website/data/services.json 中每个服务的 scheme 与配置项说明。务必注意使用 S3 等服务前必须在 CMake 配置阶段通过OPENDAL_FEATURES启用对应服务cmake -DOPENDAL_FEATURESopendal/services-s3 ...多个服务用逗号分隔例如-DOPENDAL_FEATURESopendal/services-s3,opendal/services-memory。如果未启用对应 feature运行时构造 Operator 会失败因为对应的 Rust 服务实现没有被编译进静态库。流式读取避免大对象拷贝对于大对象op.Read(path)会把全部数据拷贝进一个std::string代价较高。官方推荐改用GetReader分块读取或将Reader包装成标准std::istream使用#include opendal.hpp #include iostream #include string int main() { opendal::Operator op(memory); op.Write(data.bin, some large content); // Read in chunks via Reader. { auto reader op.GetReader(data.bin); char buf[64]; std::streamsize n reader.Read(buf, sizeof(buf)); std::cout.write(buf, n); std::cout \n; } // Or wrap as a standard istream. { opendal::ReaderStream stream(op.GetReader(data.bin)); std::string token; stream token; // reads first whitespace-delimited token std::cout token \n; } }底层实现细节见 bindings/cpp/src/reader.cppReader::Read(void *s, std::streamsize n)将 C 缓冲直接转成rust::Sliceuint8_t交给 Rust 侧read不产生中间拷贝reader.cpp#L40-L42Reader还提供ReadAt(void *s, std::streamsize n, uint64_t offset)按偏移读取以及Seek(off, dir)支持beg/cur/end三种定位方式见 reader.cpp#L49-L67ReaderStream继承std::istream内部ReaderStreamBuf以 8192 字节缓冲区实现underflow/uflow/seekoff因此可以直接配合、std::getline、tellg等标准流操作opendal.hpp#L279-L366。同步 API 中Operator::Read的注释也明确提示“该操作会产生不必要的拷贝推荐使用GetReader”opendal.hpp#L85-L89。目录遍历List 与惰性 ListerOperator::List一次性返回目录下的全部条目std::vectoropendal::Entry按名称排序GetLister则返回惰性迭代器适合大目录场景#include opendal.hpp #include iostream int main() { opendal::Operator op(memory); op.Write(dir/a.txt, a); op.Write(dir/b.txt, b); // Eager list — returns std::vectoropendal::Entry. auto entries op.List(dir/); for (const auto entry : entries) { std::cout entry.path \n; } // Lazy lister — useful for large directories. auto lister op.GetLister(dir/); for (const auto entry : lister) { std::cout entry.path \n; } }从源码看Lister通过Next()每次取回一个std::optionalEntry并实现了begin()/end()输入迭代器Lister::Iterator见 opendal.hpp#L380-L447因此可以直接用 range-for 遍历。头文件注释同时提醒从同一个Lister产生多个迭代器是未定义行为。Entry目前仅暴露path字段data_structure.hpp#L186-L193。元数据与能力内省Metadata 字段Stat(path)返回的opendal::Metadata结构定义见 data_structure.hpp#L51-L184包含基本文件信息typeEntryMode::FILE/DIR/UNKNOWN、content_lengthHTTP 风格头cache_control、content_disposition、content_md5、content_type、content_encoding、etag、last_modified均为std::optional版本信息version如 S3 的版本 ID、is_current、is_deleted。常用访问器包括IsFile()、IsDir()、ContentLength()、Etag()、LastModified()、Version()等。头文件注释特别强调Metadata 总是与特定请求上下文绑定同一路径在不同上下文中可能返回不同元数据如版本间内容长度不同并非全局状态。运行时能力检查op.Info()返回Capability结构体其字段覆盖stat、read、write、create_dir、delete_feature、copy、rename、list、presign等布尔能力以及write_multi_max_size、write_multi_min_size、write_total_max_size等数值约束data_structure.hpp#L339-L375。测试代码中正是用op_.Info().write_can_empty判断服务是否支持写入空内容后再决定是否跳过用例见 tests/behavior/read_test.cpp#L44-L59。Options 结构体对应操作的选项也定义在 data_structure.hpp 中常用字段包括ReadOptionsrangeReadRange支持Full()/Offset()/Range(start, end)/Suffix(size)四种模式、if_match/if_none_match、if_modified_since/if_unmodified_since、concurrent、chunk、gap等并发读参数WriteOptionsappend、content_type、cache_control、user_metadata、if_not_exists、concurrent、chunkDeleteOptionsversion、recursiveListOptionslimit、start_after、recursive、versions、deleted。通过 Layer 增强 Operator超时与重试虽然 Overview 文档未展开但 C Binding 已提供 layer 机制见 bindings/cpp/include/layer.hpp可在构造Operator时通过第三个参数注入底层映射到 Rust 核心的TimeoutLayer/RetryLayer见 bindings/cpp/src/layer.rs#include opendal.hpp #include layer.hpp // 超时timeout 控制非 IO 操作io_timeout 控制读写、list 等流式 IO 操作 std::vectorstd::unique_ptropendal::OperatorOption options; options.push_back(opendal::WithTimeout(std::chrono::seconds(30), std::chrono::seconds(60))); // 重试默认 jitterfalse, factor2.0, min_delay1s, max_delay1min, max_times3 options.push_back(opendal::WithRetry()); opendal::Operator op(memory, {}, std::move(options));layer.hpp 中RetryConfig的默认值为jitterfalse, factor2.0, min_delay1s, max_delay1min, max_times3layer.hpp#L65-L71。头文件注释建议同时使用重试与超时时应先传WithTimeout再传WithRetry使每次重试尝试都拥有独立的超时layer.hpp#L98-L102。异步 API可选、C20、Rust Future异步 API 是独立的可选头文件仅支持 Clang/AppleClang C20。配置阶段开启cmake -DCMAKE_C_COMPILERclang -DCMAKE_CXX_COMPILERclang \ -DOPENDAL_ENABLE_ASYNCON ..然后从opendal_async.hpp使用opendal::async::Operator。操作返回 Rust future你可以用co_await等待或通过你的异步运行时阻塞等待#include opendal_async.hpp #include cstdint #include string_view #include vector int main() { opendal::async::Operator op(memory); // Write requires a std::spanuint8_t as the second argument. std::string_view content Hello, OpenDAL!; std::vectoruint8_t buf(content.begin(), content.end()); auto write_fut op.Write(hello.txt, std::spanuint8_t(buf)); // ... await write_fut ... auto read_fut op.Read(hello.txt); // ... await read_fut ... }仓库测试tests/async_test.cpp展示了两种消费 future 的方式——用cppcoro::sync_wait阻塞等待或配合 cppcoro 的co_await协程语法cppcoro::sync_wait([]() - cppcoro::taskvoid { co_await op-CreateDir(test_async_dir/); auto dir_exists co_await op-Exists(test_async_dir/); EXPECT_TRUE(dir_exists); co_return; }());异步桥接的实现位于 bindings/cpp/src/async.rs通过cxx_async将 Rust 侧 future 暴露为 C 可等待的RustFutureRead、RustFutureWrite、RustFutureList、RustFutureBool等类型见 async.rs#L90-L118。异步Reader支持按(start, len)范围读取Lister通过Next()返回下一个条目 future见 opendal_async.hpp#L87-L154。官方提醒异步 API 比同步 API 更实验性当前方法签名以 bindings/cpp/include/opendal_async.hpp 为准。同步 API 底层则由 bindings/cpp/src/lib.rs 中的 tokio 多线程运行时驱动LazyLocktokio::runtime::Runtime见 lib.rs#L41-L46因此同步调用也可以安全地在多线程环境中使用。测试验证行为测试与异步测试仓库内置了两层测试可用于验证你的构建是否正确单元/基础测试tests/basic_test.cpp、tests/layer_test.cpp、tests/metadata_test.cpp覆盖 Operator 基本操作写、读、拷贝、重命名、删除与 layer 配置行为测试Behavior Teststests/behavior/下的read_test.cpp、write_test.cpp、delete_test.cpp、list_test.cpp以及开启异步后追加的async_read_test.cpp、async_write_test.cpp、async_list_test.cpp。它们通过op_.Info()内省能力后自适应跳过不支持的服务例如读取测试会先检查write_can_empty再决定是否跳过空文件用例见 tests/behavior/read_test.cpp。开启方式cmake -DOPENDAL_ENABLE_TESTINGON ...或直接-DOPENDAL_DEVON测试框架使用 GoogleTest异步测试额外依赖 cppcoro见 CMakeLists.txt#L189-L257。更多资源与下一步官方用户指南C Binding 的完整指南入口即本文依据的 website/docs/20-bindings/cpp/01-overview.mdGetting started 实战篇Getting started 提供可运行程序与真实后端接入的逐步说明Services 与配置各服务的 scheme 与配置键见 website/data/services.json源码bindings/cpp含include/、src/、tests/与CMakeLists.txt示例examples/cpp/getting_started.cpp 与 examples/cpp/basic.cpp 是可直接复制运行的入门代码。建议的下一步先在memory服务上跑通 Getting started 中的第一个程序然后选择一个真实后端如 S3按OPENDAL_FEATURES启用服务并填写配置表最后根据对象大小选择GetReader流式读取或GetLister惰性遍历必要时再开启异步 API 提升并发吞吐。赞分享数据存储后端【免费下载链接】opendalApache OpenDAL: One Layer, All Storage.项目地址https://gitcode.com/gh_mirrors/op/opendal点击查看免费下载相关推荐Apache OpenDAL OCaml Binding 使用指南从环境搭建到流式读写与能力自省Apache OpenDAL OCaml Binding 使用指南从环境搭建到流式读写与能力自省 Apache OpenDAL™ OCaml Binding数据存储后端Apache OpenDAL C Binding 接入指南用一套 C API 读写 50 存储服务Apache OpenDAL C Binding 接入指南用一套 C API 读写 50 存储服务 Apache OpenDALOne Layer数据存储后端Apache OpenDAL Python Binding 实战指南从安装、统一读写对象存储到异步 API 与 Layers 扩展Apache OpenDAL Python Binding 实战指南从安装、统一读写对象存储到异步 API 与 Layers 扩展 本文以 Apache Op数据存储后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考