Java 反应器-如何压缩通量<;ByteBuffer>;在飞行中?

Java 反应器-如何压缩通量<;ByteBuffer>;在飞行中?,java,spring-webflux,project-reactor,Java,Spring Webflux,Project Reactor,我需要在没有中间存储的情况下读写压缩(gzip/brotli)流。数据以Flux格式从底层接收。数据足够大,无法进行缓冲。如何动态压缩Flux,而不必将完整数据存储在内存中或写入磁盘?您希望避免缓冲完整数据,但可以归档每个ByteBuffer块,或者,如果块足够小,则将块合并到组中,然后归档 这不需要太长的内存,但会压缩数据。 实际的压缩级别取决于源数据的内容和归档前整合的块数。我认为,您可以手动调整它以获得最佳比例 可能代码的示例如下所示: public class Test_GzipFlux

我需要在没有中间存储的情况下读写压缩(gzip/brotli)流。数据以
Flux
格式从底层接收。数据足够大,无法进行缓冲。如何动态压缩
Flux
,而不必将完整数据存储在内存中或写入磁盘?

您希望避免缓冲完整数据,但可以归档每个ByteBuffer块,或者,如果块足够小,则将块合并到组中,然后归档

这不需要太长的内存,但会压缩数据。 实际的压缩级别取决于源数据的内容和归档前整合的块数。我认为,您可以手动调整它以获得最佳比例

可能代码的示例如下所示:

public class Test_GzipFlux {

/**
 * Returns Flux of gzip-ed buffers after (optional) buffer consolidation
 * @param inFlux input stream of buffers
 * @param consolidatedBufCount number of buffers to consolidate before gzip-ing
 */
public static Flux<ByteBuffer> gzipFlux(Flux<ByteBuffer> inFlux, 
                                        int consolidatedBufCount, int outChunkMaxLength) {
    return inFlux.buffer(consolidatedBufCount)
                 .map(inList->zipBuffers(inList, outChunkMaxLength));
}

/**
 * Consolidates buffers from input list, applies gzip, returns result as single buffer
 * @param inList portion of chunks to be consolidated
 * @param outChunkMaxLength estimated length of output chunk. 
 *        !!! to avoid pipe deadlock, this length to be sufficient
 *        !!! for consolidated data after gzip 
 */
private static ByteBuffer zipBuffers(List<ByteBuffer> inList, int outChunkMaxLength) {
    try {
        PipedInputStream pis = new PipedInputStream(outChunkMaxLength);
        GZIPOutputStream gos = new GZIPOutputStream(new PipedOutputStream(pis));

        for (var buf: inList) {
            gos.write(buf.array());
        }
        gos.close();
        byte[] outBytes = new byte[pis.available()];
        pis.read(outBytes);
        pis.close();
        return ByteBuffer.wrap(outBytes);
    } catch (IOException e) {
        throw new RuntimeException(e.getMessage(), e);
    }       
}

private static void test() {
    int inLength  = ... // actual full length of source data
    Flux<ByteBuffer> source = ... // your source Flux
    
    // these are parameters for your adjustment
    int consolidationCount = 5;
    int outChunkMaxLength= 30 * 1024;

    Flux<ByteBuffer> result = gzipFlux(source,consolidationCount, outChunkMaxLength);           

    int outLen = result.reduce(0, (res, bb) -> res + bb.array().length).block();
    System.out.println("ratio=" + (double)inLength/outLen);
}
}
公共类测试{
/**
*返回(可选)缓冲区合并后gzip ed缓冲区的流量
*@param缓冲区流入输入流
*@param consolidatedBufCount在gzip初始化之前要合并的缓冲区数
*/
公共静态通量gzip通量(通量流入,
int合并Bufcount,int outChunkMaxLength){
返回流入缓冲区(合并缓冲区)
.map(inList->zipBuffers(inList,outChunkMaxLength));
}
/**
*合并输入列表中的缓冲区,应用gzip,将结果作为单个缓冲区返回
*@param inList要合并的块的部分
*@param outChunkMaxLength输出区块的估计长度。
*!!!为避免管道死锁,此长度足够长
*!!!用于gzip之后的合并数据
*/
私有静态ByteBuffer zipBuffers(列表inList,int outChunkMaxLength){
试一试{
PipedInputStream pis=新的PipedInputStream(outChunkMaxLength);
GZIPOutputStream gos=新的GZIPOutputStream(新的PipedOutputStream(pis));
用于(变量buf:inList){
write(buf.array());
}
gos.close();
byte[]outBytes=新字节[pis.available()];
pis.read(输出字节);
pis.close();
返回ByteBuffer.wrap(outBytes);
}捕获(IOE异常){
抛出新的RuntimeException(e.getMessage(),e);
}       
}
专用静态空隙试验(){
int inLength=…//源数据的实际完整长度
通量源=…//您的源通量
//这些是供您调整的参数
int consolidationCount=5;
int-outChunkMaxLength=30*1024;
通量结果=gzip通量(源、合并计数、outChunkMaxLength);
int outLen=result.reduce(0,(res,bb)->res+bb.array().length.block();
System.out.println(“比值=”+(双精度)输入长度/输出长度);
}
}

您应该能够使用JDK中的
平减指数
类-自11年以来,它能够直接使用
ByteBuffer