如何直接上传从JNI获取的Direct Buffer至S3
直接上传Direct Buffer数据至S3的方案
你可以通过以下两种方式避免Direct Buffer到JVM堆内存的拷贝,实现直接上传:
一、针对当前使用的jclouds BlobStore API
当前代码中使用ByteArrayPayload必须依赖堆内存的byte[],导致强制拷贝。你可以自定义一个基于Direct ByteBuffer的Payload实现,让上传逻辑直接读取共享内存映射的缓冲区:
1. 自定义ByteBufferPayload类
import org.jclouds.io.Payload; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; public class ByteBufferPayload implements Payload { private final ByteBuffer buffer; private final long contentLength; public ByteBufferPayload(ByteBuffer buffer) { // 创建只读副本,避免原buffer的位置被修改 this.buffer = buffer.asReadOnlyBuffer(); // 用remaining()获取实际有效数据长度(而非capacity,避免上传共享内存中空的部分) this.contentLength = this.buffer.remaining(); } @Override public InputStream openStream() { // 返回直接读取ByteBuffer的InputStream,无拷贝 return new ByteBufferInputStream(buffer.duplicate()); } @Override public Long getContentLength() { return contentLength; } @Override public String getContentType() { return null; } @Override public void setContentType(String contentType) {} @Override public void close() throws IOException {} // 内部类:直接读取ByteBuffer的InputStream实现 private static class ByteBufferInputStream extends InputStream { private final ByteBuffer buf; public ByteBufferInputStream(ByteBuffer buf) { this.buf = buf; } @Override public int read() throws IOException { return buf.hasRemaining() ? (buf.get() & 0xFF) : -1; } @Override public int read(byte[] b, int off, int len) throws IOException { if (!buf.hasRemaining()) return -1; len = Math.min(len, buf.remaining()); buf.get(b, off, len); return len; } } }
2. 修改上传代码
替换ByteArrayPayload为自定义的ByteBufferPayload,同时修正数据长度的获取逻辑:
public String uploadByteBuffer(String container, String objectKey, ByteBuffer bb) { BlobStoreContext context = getBlobStoreContext(); BlobStore blobStore = context.getBlobStore(); // 创建只读副本,保护原buffer的position/limit ByteBuffer readOnlyBuf = bb.asReadOnlyBuffer(); Payload payload = new ByteBufferPayload(readOnlyBuf); Blob blob = blobStore.blobBuilder(objectKey) .payload(payload) .contentLength(readOnlyBuf.remaining()) .build(); blobStore.putBlob(container, blob); return objectKey; }
二、改用AWS官方SDK v2(更简洁)
如果你可以切换到AWS官方的SDK v2,它原生支持Direct ByteBuffer的直接上传,无需自定义实现:
import software.amazon.awssdk.core.SdkBytes; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import java.nio.ByteBuffer; public String uploadToS3(String bucket, String key, ByteBuffer bb) { try (S3Client s3Client = S3Client.create()) { // 创建只读副本,避免修改原buffer的状态 ByteBuffer readOnlyBuf = bb.asReadOnlyBuffer(); PutObjectRequest request = PutObjectRequest.builder() .bucket(bucket) .key(key) .contentLength((long) readOnlyBuf.remaining()) .build(); // 直接传入Direct ByteBuffer,SDK内部无拷贝读取 s3Client.putObject(request, software.amazon.awssdk.core.sync.RequestBody.fromByteBuffer(readOnlyBuf)); return key; } }
关键注意事项
- 有效数据长度:一定要用
buffer.remaining()而非capacity(),因为共享内存总大小是200MB,但实际有效数据只有50MB,避免上传空数据。 - Buffer状态保护:每次上传前用
asReadOnlyBuffer()创建副本,防止上传逻辑修改原buffer的position/limit,影响后续操作。 - 共享内存生命周期:上传完成前不要释放共享内存,否则会导致读取数据时出现错误。
内容的提问来源于stack exchange,提问作者Bhavana Shah
相关产品推荐
相关产品推荐

