Source:
private Mono<Response<BlockBlobItem>> uploadInChunks(BlockBlobAsyncClient blockBlobAsyncClient, Flux<ByteBuffer> data, ParallelTransferOptions parallelTransferOptions, BlobHttpHeaders headers, Map<String, String> metadata, AccessTier tier, BlobRequestConditions accessConditions) { AtomicLong totalProgress = new AtomicLong(); Lock progressLock = new ReentrantLock(); UploadBufferPool pool = new UploadBufferPool(parallelTransferOptions.getNumBuffers(), parallelTransferOptions.getBlockSize()); Flux<ByteBuffer> chunkedSource = data .filter(ByteBuffer::hasRemaining) .flatMapSequential(buffer -> { if (buffer.remaining() <= parallelTransferOptions.getBlockSize()) { return Flux.just(buffer); } int numSplits = (int) Math.ceil(buffer.remaining() / (double) parallelTransferOptions.getBlockSize()); return Flux.range(0, numSplits) .map(i -> { ByteBuffer duplicate = buffer.duplicate().asReadOnlyBuffer(); duplicate.position(i * parallelTransferOptions.getBlockSize()); duplicate.limit(Math.min(duplicate.limit(), (i + 1) * parallelTransferOptions.getBlockSize())); return duplicate; }); }); return chunkedSource.concatMap(pool::write) .concatWith(Flux.defer(pool::flush)) .flatMapSequential(buffer -> { Flux<ByteBuffer> progressData = ProgressReporter.addParallelProgressReporting(Flux.just(buffer), parallelTransferOptions.getProgressReceiver(), progressLock, totalProgress); final String blockId = Base64.getEncoder().encodeToString( UUID.randomUUID().toString().getBytes(UTF_8)); return blockBlobAsyncClient.stageBlockWithResponse(blockId, progressData, buffer.remaining(), null, accessConditions.getLeaseId()) .map(x -> blockId) .doFinally(x -> pool.returnBuffer(buffer)) .flux(); }) .collect(Collectors.toList()) .flatMap(ids -> blockBlobAsyncClient.commitBlockListWithResponse(ids, headers, metadata, tier, accessConditions)); }

Target:
private Mono<Response<BlockBlobItem>> uploadInChunks(BlockBlobAsyncClient blockBlobAsyncClient, Flux<ByteBuffer> data, ParallelTransferOptions parallelTransferOptions, BlobHttpHeaders headers, Map<String, String> metadata, AccessTier tier, BlobRequestConditions accessConditions) { AtomicLong totalProgress = new AtomicLong(); Lock progressLock = new ReentrantLock(); UploadBufferPool pool = new UploadBufferPool(parallelTransferOptions.getNumBuffers(), parallelTransferOptions.getBlockSize()); Flux<ByteBuffer> chunkedSource = data .flatMapSequential(buffer -> { if (buffer.remaining() <= parallelTransferOptions.getBlockSize()) { return Flux.just(buffer); } int numSplits = (int) Math.ceil(buffer.remaining() / (double) parallelTransferOptions.getBlockSize()); return Flux.range(0, numSplits) .map(i -> { ByteBuffer duplicate = buffer.duplicate().asReadOnlyBuffer(); duplicate.position(i * parallelTransferOptions.getBlockSize()); duplicate.limit(Math.min(duplicate.limit(), (i + 1) * parallelTransferOptions.getBlockSize())); return duplicate; }); }); return chunkedSource.concatMap(pool::write) .concatWith(Flux.defer(pool::flush)) .flatMapSequential(buffer -> { Flux<ByteBuffer> progressData = ProgressReporter.addParallelProgressReporting(Flux.just(buffer), parallelTransferOptions.getProgressReceiver(), progressLock, totalProgress); final String blockId = Base64.getEncoder().encodeToString( UUID.randomUUID().toString().getBytes(UTF_8)); return blockBlobAsyncClient.stageBlockWithResponse(blockId, progressData, buffer.remaining(), null, accessConditions.getLeaseId()) .map(x -> blockId) .doFinally(x -> pool.returnBuffer(buffer)) .flux(); }) .collect(Collectors.toList()) .flatMap(ids -> blockBlobAsyncClient.commitBlockListWithResponse(ids, headers, metadata, tier, accessConditions)); }