首页 / 资讯中心 / 文章详情

华为OBS Java SDK离线接入指南:从依赖导入到断点续传实践

华为OBS Java SDK离线接入指南:从依赖导入到断点续传实践 ★ FEATURED ARTICLE
简介面向 Java 开发者的华为云 OBS 对象存储服务 SDK 资源包版本为 3.20.3可帮助快速实现文件上传、下载、桶管理及对象生命周期管理等云存储操作也适合作为企业项目二次开发的基线工具。压缩包共 766 个文件整体仅 7.31MB其中包含 449 个 HTML 格式的 API 文档页面、286 个 Java 示例与源码文件、10 个 XML 工程及日志配置、9 个 JAR 依赖库另有 LICENSE、MD 说明等配套文件目录结构清晰便于按需查阅。资源内置完整 SDK 库、Maven 依赖描述、可直接运行的示例代码以及官方 javadoc 文档开发者可据此搭建 Maven 工程理解核心客户端接口的调用方式并参考示例完成上传、下载、列举桶、删除对象等典型操作日志配置还支持灵活调整输出级别便于联调阶段问题定位。已有 1833 人学习下载适合刚接触华为云 OBS 的入门者以及需要深度集成 OBS 服务的后端工程师参考使用。1. 华为 OBS SDK 3.20.3Java 接入对象存储时的离线首选包上个月给一个内网客户做数据迁移安全策略禁止开发机访问 Maven 中央仓库所有第三方 jar 要人工审核后进内部制品库。我把申请递上去时顺手下载了官方 esdk-obs-java-3.20.3.zip解压后主包、依赖、源码和中文文档齐了离线导入一次通过。这个 zip 就是华为 OBS SDK 的 Java 发行包3.20.3 版本提供 ObsClient 全套能力覆盖桶操作、上传下载、断点续传和管理策略。对于不能实时拉取依赖的维护项目手工导入 zip 反而比 Maven 更可控适合内网部署、遗留系统升级、需要审计依赖的 Java 工程师新手按包内示例也能跑通第一行上传代码。2. 解包 esdk-obs-java-3.20.3jar 清单、依赖取舍与本地仓库导入2.1 拆开 zip 先别急着全量引入先认目录拿到 zip 后很多人直接把里面所有 jar 拖进项目 libs结果启动时 log4j 或者 okhttp 冲突。我拆过几个 esdk-obs-java 发行包目录结构大同小异核心是 esdk-obs-java-3.20.3.jar运行期依赖在 lib 或 third_party 目录下另外会附 source 包、README、API 文档和依赖清单。下面这张表是我一般会先核对的几项。文件/目录内容离线审核时看什么esdk-obs-java-3.20.3.jar主库ObsClient、PutObjectRequest 等核心类都在里面确认版本号与文档一致lib/okhttp-.jar、gson-.jarHTTP 和 JSON 依赖看版本是否和项目现有 jar 冲突esdk-obs-java-3.20.3-source.jar源码包排查问题时反查实现doc/中文 API 手册与示例先查支持的 Operationsdependency-list.txt依赖清单人工审计时逐项打勾先读依赖清单再决定引哪些 jar。OBS SDK 3.x 的 HTTP 层基于 OkHttp序列化用了 Gson日志用 Log4j2这三类是冲突高发区。如果你项目里已经有 okhttp 3.14 以上版本不要直接覆盖先看 SDK 内置版本与你的代码有没有调用差异。提示离线审核时把 dependency-list.txt 和 source jar 一起归档后续等保检查或制品溯源都要用。2.2 从 libs 走向内部 Maven两种导入方式对比第一种方式IDE 项目直接把 jar 放入 libs 目录并加入 module 依赖。速度快但每个项目都要重复手工步骤构建产物也不统一我一般只用来做本地验证。第二种方式更可靠把 SDK 主包和第三方依赖统一安装到内部 Maven 仓库pom 里只写一个坐标。# 安装 SDK 主包 mvn install:install-file \ -Dfileesdk-obs-java-3.20.3.jar \ -DgroupIdcom.huaweicloud \ -DartifactIdesdk-obs-java \ -Dversion3.20.3 \ -Dpackagingjar # 如果有源码包需要一起发布再加 -Dclassifiersources mvn install:install-file \ -Dfileesdk-obs-java-3.20.3-source.jar \ -DgroupIdcom.huaweicloud \ -DartifactIdesdk-obs-java \ -Dversion3.20.3 \ -Dpackagingjar \ -Dclassifiersourcesinstall-file会安装一个 jar相当于把本地文件“发布”到本地仓库。-DgroupId/-DartifactId/-Dversion必须和后续 pom 坐标一致否则构建时找不到。我不建议把版本改写成 3.20.3-RELEASE 这类自定义版本问题定位时对不上原始包会很难受。之后项目 pom 里声明依赖dependency groupIdcom.huaweicloud/groupId artifactIdesdk-obs-java/artifactId version3.20.3/version /dependency如果内部 Maven 仓库还没有 okhttp、gson 这些第三方依赖同样需要先把它们安装进仓库否则 Maven 构建时依旧解析失败。此时可以执行mvn dependency:tree看 SDK 的传递依赖再决定逐个 install 还是直接用项目内已有版本覆盖。2.3 依赖冲突高发点OkHttp 与 Log4j2 的取舍我在给老 Spring Boot 项目接 OBS SDK 时遇到最多的是NoSuchMethodError: okhttp3.RequestBody.create。原因很简单系统里已有旧版 okhttpOBS SDK 编译时用了新版的静态方法运行时加载了老 class直接炸。解决方式是先从依赖里排除 SDK 自带的 okhttp统一走项目里的版本。dependency groupIdcom.huaweicloud/groupId artifactIdesdk-obs-java/artifactId version3.20.3/version exclusions exclusion groupIdcom.squareup.okhttp3/groupId artifactIdokhttp/artifactId /exclusion /exclusions /dependency排除前要确认项目现有 okhttp 版本不被 SDK 调用的新 API 影响否则会出现暗坑。Log4j2 冲突也一样如果项目已经有 log4j2需要检查版本是否高于 SDK 要求实在无法统一时可以在部署层做 shiro/日志桥接但那是最后手段。更稳妥的做法是先跑一遍mvn dependency:tree -Dincludescom.squareup.okhttp3看清依赖来源再决定排除哪一端。除了 Maven老 Web 项目常直接拷贝 jar 到WEB-INF/lib。这时我建议只拷贝 dependency-list.txt 里标为 compile 的 jar并按主包在前、第三方在后的顺序加入。主包是否完整可以用jar tf快速验证jar tf esdk-obs-java-3.20.3.jar | grep ObsClient能看到com/obs/services/ObsClient.class基本没问题。这一步能拦住绝大多数“jar 包损坏”或“下载不完整”的翻车。3. 初始化 ObsClientAK/SK、Endpoint 与连接参数的一次配好3.1 创建 ObsClient最小可用代码与线程安全说明接入 OBS 的第一步是拿到ObsClient。最小代码如下import com.obs.services.ObsClient; public class ObsConnection { public static void main(String[] args) throws Exception { String endpoint https://obs.cn-north-4.myhuaweicloud.com; String ak System.getenv(OBS_AK); String sk System.getenv(OBS_SK); ObsClient client new ObsClient(ak, sk, endpoint); System.out.println(client.listBuckets()); client.close(); } }ObsClient封装了 HTTP 调用、签名计算、连接池和重试。构造函数只做参数初始化不会立刻联网上方代码里的listBuckets()会真实调用一次 OBS 服务用来验证 AK/SK 是否有效。ObsClient 是线程安全的应用启动时创建一个全局单例即可不要每次上传都 new。每 new 一个都会占住一个连接池和对应线程资源调用频率高时很容易把 JVM 的句柄数跑上去。如果想控制超时、连接池大小和重试次数要先用ObsConfigurationObsConfiguration config new ObsConfiguration(); config.setEndPoint(https://obs.cn-north-4.myhuaweicloud.com); config.setConnectionTimeout(10000); config.setSocketTimeout(30000); config.setMaxConnections(500); config.setMaxErrorRetry(3); ObsClient client new ObsClient(ak, sk, config);这里注意方法名是setEndPoint别写成setEndpointIDE 提示时容易看走眼。connectionTimeout是建立 TCP 连接的超时socketTimeout是每次读响应的超时maxConnections是连接池上限maxErrorRetry是网络层错误的重试次数。重试次数不是越大越好接口幂等性差时重试会放大脏数据风险。3.2 AK/SK、Endpoint 与 Region 的关系Endpoint 决定了你的请求发到哪个区域的 OBS 服务端。常见区域对应关系如下区域Endpoint华北-北京四obs.cn-north-4.myhuaweicloud.com华南-广州obs.cn-south-1.myhuaweicloud.com华东-上海一obs.cn-east-3.myhuaweicloud.comEndpoint 不要带桶名也不要带端口号。错误写法是https://my-bucket.obs.cn-north-4.myhuaweicloud.com这样签名时 Host 会多一层服务端解析后大概率报 301 或 NoSuchBucket。如果 VPC 内网访问应该用对应区域的内网域名AK/SK 不需要变化。另外桶是有区域属性的。用北京四的 Endpoint 去访问华南广州的桶请求会被重定向或直接拒绝。我应该把区域配置放到统一配置中心而不是写死在代码里。跨区域访问场景还要考虑流量费用Endpoint 只是技术入口不代表业务上可以随意跨区拉文件。3.3 AK/SK 不要写死在代码里环境变量与临时凭证最常见的错误是把 AK/SK 直接粘贴在代码里然后代码被传到 Git 仓库。我习惯先从环境变量读取String ak System.getenv(OBS_AK); String sk System.getenv(OBS_SK); if (ak null || sk null) { throw new IllegalStateException(missing OBS_AK or OBS_SK in env); }在本地开发时可以把密钥放在~/.obs/credentials这类外部文件生产环境在华为云 CCE/ECS 上运行时优先使用委托方式获取临时凭证这样即使容器被攻破拿到的也是短期凭证而不是永久 AK/SK。临时凭证方案一般涉及华为云 IAM 的 STS 服务SDK 里对应ObsClient的 credentials provider 参数。换凭证时不需要重建 ObsClient只需要确认新凭证有对应的 obs:Object:* 权限。如果使用独立的配置中心读取后也要保证进程内存中只有一份不要反复读取。密钥明文落盘的风险比代码硬编码更难发现尤其是配置文件被镜像打包时很容易一并在镜像里泄露出去。4. 上传下载实战PutObject、GetObject 与断点续传的参数选择4.1 简单上传与下载流式处理必须记住关闭响应体普通小文件直接使用PutObjectRequest最省事。以字符串上传为例PutObjectRequest putRequest new PutObjectRequest( my-bucket, logs/app-2025-01.log, 2025-01-01 00:00:00); obsClient.putObject(putRequest);这个构造函数里填的是桶名、对象名、字符串内容。对象名即 keylogs/只是模拟目录层级OBS 并没有真实目录概念。如果上传本地文件代码变成File file new File(app-2025-01.log); PutObjectRequest putRequest new PutObjectRequest(my-bucket, logs/ file.getName(), file); ObjectMetadata metadata new ObjectMetadata(); metadata.setContentType(text/plain); metadata.setContentLength(file.length()); putRequest.setMetadata(metadata); obsClient.putObject(putRequest);metadata.setContentType显式声明文件类型避免浏览器下载.log文件时被当成纯文本展示。setContentLength一般会自动计算手动设置时要注意和文件实际长度一致否则可能出现请求 body 和服务端长度对不上。下载时服务端返回的是ObsObject真正的数据在getObjectContent()流里GetObjectRequest getRequest new GetObjectRequest(my-bucket, logs/app-2025-01.log); ObsObject object obsClient.getObject(getRequest); try (InputStream in object.getObjectContent()) { Files.copy(in, Paths.get(downloads/app-2025-01.log), StandardCopyOption.REPLACE_EXISTING); } catch (IOException e) { throw new RuntimeException(download failed, e); }getObject不会把整个文件读进内存而是返回一个可读流。这个流必须被读取并关闭否则 TCP 连接无法回到连接池。程序跑几天后连接数和文件句柄会肉眼可见地增长。这里用 try-with-resources 是最稳妥的关闭方式不要在 finally 里再写一遍close()容易重复关闭。4.2 大文件与弱网uploadFile 和 downloadFile 断点续传超过 100MB 的文件不建议用putObject(File)。这个接口适合小对象网络抖动一次就要重来。OBS Java SDK 提供了专门的大文件接口示例代码UploadFileRequest request new UploadFileRequest( my-bucket, archives/backup.zip, D:/data/backup.zip); request.setTaskNum(5); request.setPartSize(10 * 1024 * 1024); request.setEnableCheckpoint(true); obsClient.uploadFile(request);对应的下载DownloadFileRequest request new DownloadFileRequest( my-bucket, archives/backup.zip, D:/data/download/backup.zip); request.setTaskNum(8); request.setEnableCheckpoint(true); request.setPartSize(10 * 1024 * 1024); obsClient.downloadFile(request);taskNum是并发分片数partSize是单个分片大小。两者共同决定内存占用和并发压力。我一般把partSize设在 5~10MBtaskNum不超过 10弱网环境下 taskNum 太大反而加剧超时因为每个分片都在抢带宽。enableCheckpoint开启后会在本地生成分片进度记录中断后再次调用会从断点继续。但要注意如果业务文件内容变了必须清掉旧 checkpoint 记录否则可能把不同版本的分片拼接在一起得到脏数据。4.3 怎么选上传方式简单上传、流式上传、断点续传的边界方式适用场景注意点putObject(File)小于 100MB 的普通文件简单直接不能断点续传putObject(InputStream)请求流、管道数据需要自己处理异常关闭uploadFile大文件、弱网、需要重试分片数不能超过 10000partSize 影响并发追加上传 AppendObject日志顺序追加不支持并发追加追加次数有限制选择依据主要看文件大小和网络稳定性。小文件在弱网环境下直接重试几次成本也不高大文件必须用uploadFile它内部会做分片并发和进度持久化。如果你在代码里给多个线程分别调putObject(File)上传同一个大文件那不是断点续传只会多占内存和带宽。另外uploadFile底层走的是 OBS 的 multipart upload对同一对象发起的分片上传任务数量有限制。如果长时间只传不合并或者频繁取消服务端会积累未完成的分片产生额外存储费用。生产环境要定期清理未完成的分片任务或者监控桶里的 multipart 数量。5. 避坑与常见问题排查OBS Java SDK 最常翻车的五个细节5.1 桶名带上了 Endpoint301/404 反复出现现象请求返回 301或者桶明明存在却报 NoSuchBucket。原因Endpoint 写成了“桶名 域名”。SDK 的 endpoint 只接受区域域名桶名是请求参数里的独立项把桶名拼进去后 Host 头就多了一层签名验证自然失败。解决Endpoint 统一填https://obs.cn-north-4.myhuaweicloud.com桶名只写在PutObjectRequest/GetObjectRequest的构造参数里。CDN 或自定义域名场景需要单独配置 path style不应该用普通 endpoint 硬拼。5.2 AK/SK 正确但还是 AccessDenied现象本地用同样 AK/SK 能访问部署到客户环境后总是 AccessDenied。原因IAM 用户权限不够、临时凭证作用域不对或者客户端系统时间与服务器偏差过大导致签名过期。也可能桶策略限制了来源 IP 或指定了请求头。解决先检查 IAM 策略是否包含obs:Object:PutObject、obs:Object:GetObject。如果用的是临时凭证确认角色策略是否完整。然后看服务器系统时间偏差超过 15 分钟就先做时间同步。OBS 签名用的是 UTC 时间不会自动跟随本地时区时间偏移是最隐蔽的 AccessDenied 原因。5.3 大文件上传内存爆掉现象用putObject(new File(...))上传 500MB 文件JVM 内存飙升并出现 OOM加-Xmx4g也没用。原因简单上传接口在构造请求时可能会额外加载整个文件或较大的缓冲区域同时大文件本身也增加了 IO 压力堆内存很快被占满。解决改用UploadFileRequest分片上传partSize控制在 5~10MBtaskNum控制在 2~8。同时在ObsConfiguration里调整maxConnections避免每个分片线程都持有大缓冲。如果业务上必须用简单上传就用InputStream并显式控制读缓冲不要直接把整个 File 交给 SDK。5.4 代理环境下连接池被占满现象内网请求走 HTTP 代理刚开始正常一段时间后所有请求 SocketTimeout。原因ObsClient 默认使用 okhttp 连接池maxConnections 默认值不够大。代理环境下每次请求都要额外握手长连接失效后大量建连互相争抢最终连接池被占满。解决在ObsConfiguration中显式配置代理和连接池参数config.setProxyHost(proxy.internal); config.setProxyPort(8080); config.setMaxConnections(200);maxConnections 建议按业务峰值的 1.2 倍左右设置。同时观察 okhttp 的 eviction 日志确认代理本身没有并发连接数限制。如果代理还做了 TLS 拦截还需要把相应证书导入到 JVM 的 trustStore否则会报 SSLHandshakeException。5.5 依赖冲突导致 NoSuchMethodError现象项目启动后调用 OBS SDK 报NoSuchMethodError: okhttp3.RequestBody.create(...)。原因项目里另一个依赖带了旧版 okhttp把 OBS SDK 依赖的 okhttp 版本覆盖了运行时加载的类缺少新版方法。解决在 pom 里排除 OBS SDK 自带的 okhttp统一用项目内较新的版本如果项目内版本过旧就反过来升级项目依赖。查看依赖树是关键mvn dependency:tree -Dincludescom.squareup.okhttp3看到重复的 okhttp 版本后再决定排除哪一边。日志冲突同理优先保证 log4j2 高版本被加载否则 SDK 的日志输出会静默失败排查问题时看不到任何请求信息。6. 进阶用法并发上传与日志验证让 SDK 吞吐量真正跑起来6.1 线程池并发上传配合连接池和日志验证ObsClient 是线程安全的所以并发上传小文件时可以直接用固定线程池。常见做法是ExecutorService pool Executors.newFixedThreadPool(8); ListPath files ...; for (Path path : files) { pool.submit(() - { obsClient.putObject(my-bucket, path.getFileName().toString(), path.toFile()); }); } pool.shutdown(); pool.awaitTermination(30, TimeUnit.MINUTES);线程数不要盲目往上涨。ObsConfiguration 里的maxConnections要跟着调大否则所有线程都在等连接池整体吞吐反而下降。对于大文件UploadFileRequest内部已经做了分片并发不要再在外面套一层多线程去并发刷同一个文件分片竞争会拖慢速度。上线前我习惯把日志调出来看一眼OBS SDK 内部使用 log4j2logger 名称一般是com.obs.services。把这一级调到 DEBUG可以看到签名的 Host、请求路径和响应状态码。确认请求路径里没有多余的桶名再放开并发压测。验证上传是否成功不一定要下载回来。可以读对象元数据做一致性判断ObjectMetadata meta obsClient.getObjectMetadata(my-bucket, logs/app.log); System.out.println(meta.getContentLength());把元数据里的 contentLength 和本地文件大小比对能快速发现未传完或传错对象的问题。再用v2: obs://bucket/key这类方式做一次下载校验比较稳妥。之前我为了追求并发把线程池开到 32连接池没调压测到第 20 分钟就开始大片超时后来把 maxConnections 和线程数一起控制在 16实测吞吐才稳定下来。从那以后我每次接 OBS SDK 都强制做一遍 20 分钟并发压测确认 CPU、句柄和超时三个指标没有异常才敢上线。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站