You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何直接上传从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 07:10:31