Java BlobStore completeMultipartUpload方法ETag为空触发空指针异常
问题概述
本地环境执行多部分上传保存.csv文件时,前9个MultipartPart的blobPart.getMetadata().getETag()返回正常,但第10个及之后的分片ETag始终为空,进而触发NullPointerException。所有分片的添加方式一致且属性配置正确,其他同事的运行环境无此问题。
相关代码
@Override public String completeMultipartUpload(MultipartUpload mpu, List<MultipartPart> parts) { ImmutableList.Builder<Blob> blobs = ImmutableList.builder(); long contentLength = 0; Hasher md5Hasher = Hashing.md5().newHasher(); for (MultipartPart part : parts) { Blob blobPart = getBlob(mpu.containerName(), MULTIPART_PREFIX + mpu.id() + "-" + mpu.blobName() + "-" + part.partNumber()); contentLength += blobPart.getMetadata().getContentMetadata().getContentLength(); blobs.add(blobPart); md5Hasher.putBytes(BaseEncoding.base16().lowerCase().decode(blobPart.getMetadata().getETag())); } }
已尝试的排查操作
- 重新安装IntelliJ
- 重新安装/更换Java版本
- 移除并重新安装所有依赖库
怀疑方向
推测问题可能与编码相关,代码中使用了BaseEncoding.base16(),且正常运行的同事位于其他国家,可能存在Locale或字符编码差异。
堆栈信息
java.lang.NullPointerException: null
at com.google.common.base.Preconditions.checkNotNull(Preconditions.java:878)
at com.google.common.io.BaseEncoding$StandardBaseEncoding.trimTrailingPadding(BaseEncoding.java:676)
at com.google.common.io.BaseEncoding.decodeChecked(BaseEncoding.java:231)
at com.google.common.io.BaseEncoding.decode(BaseEncoding.java:217)
at org.jclouds.blobstore.config.LocalBlobStore.completeMultipartUpload(LocalBlobStore.java:843)
解决思路与方案
1. 验证分片Blob的命名与存储完整性
- 检查第10个及以上分片的Blob名称生成逻辑:确认
part.partNumber()返回的编号格式(如是否带前导零),对比同事环境下的分片命名规则,排查是否因命名格式差异导致无法正确读取Blob元数据。 - 手动检查本地LocalBlobStore的存储目录,确认第10个及之后的分片文件是否存在,以及对应的元数据中ETag字段是否被正确写入。
2. 排查编码与Locale差异
- 对比本地与同事的JVM默认Locale设置:执行
System.out.println(Locale.getDefault())查看本地Locale,若与同事不同,尝试临时修改JVM启动参数(如-Duser.language=en -Duser.region=US)验证问题是否消失。 - 显式指定字符编码:在处理ETag字符串时,确保使用统一的字符集(如UTF-8),避免依赖系统默认编码。
3. 分析LocalBlobStore的元数据写入逻辑
- 查看
LocalBlobStore中分片上传时ETag的生成与写入代码,确认是否在分片编号超过9时,元数据写入出现异常(例如本地文件系统路径长度限制、文件名特殊字符处理差异)。 - 若LocalBlobStore基于文件系统存储元数据,直接查看对应分片的元数据文件内容,确认ETag字段是否为空或存在格式错误。
4. 代码层面添加防御性处理
在调用decode前增加非空校验与日志记录,既避免NPE,也能获取更详细的排查信息:
@Override public String completeMultipartUpload(MultipartUpload mpu, List<MultipartPart> parts) { ImmutableList.Builder<Blob> blobs = ImmutableList.builder(); long contentLength = 0; Hasher md5Hasher = Hashing.md5().newHasher(); for (MultipartPart part : parts) { Blob blobPart = getBlob(mpu.containerName(), MULTIPART_PREFIX + mpu.id() + "-" + mpu.blobName() + "-" + part.partNumber()); contentLength += blobPart.getMetadata().getContentMetadata().getContentLength(); blobs.add(blobPart); String eTag = blobPart.getMetadata().getETag(); if (eTag == null) { throw new IllegalStateException("ETag is missing for part number: " + part.partNumber() + ", blob name: " + blobPart.getName()); } md5Hasher.putBytes(BaseEncoding.base16().lowerCase().decode(eTag)); } }
5. 对比操作系统与文件系统差异
- 检查本地与同事的操作系统类型(如Windows vs macOS/Linux),Windows文件系统对文件名长度、特殊字符的限制与类Unix系统不同,可能导致分片文件或元数据存储异常。
- 确认本地文件系统的大小写敏感性,若与同事环境不同,排查是否因文件名大小写问题导致无法正确读取Blob。
内容的提问来源于stack exchange,提问作者Silny ToJa

