@Override
    public void end(Handler<AsyncResult<Void>> handler) {
        Future<Void> result = vertx.executeBlocking(() -> {
            synchronized (BlobWriteStream.this) {
                if (exception != null) {
                    throw new RuntimeException(exception);
                }

                ByteBuf lastChunk = chunkBuffer.slice(0, position).getByteBuf();
                if (resourceUpload == null) {
                    log.info("Resource is too small for multipart upload, sending as a regular blob");
                    try (InputStream chunkStream = new ByteBufInputStream(lastChunk)) {
                        metadata = resourceService.putFile(resource, chunkStream.readAllBytes(), etag, contentType, author);
                    }
                } else {
                    if (position != 0) {
                        resourceUpload.addChunk(lastChunk);
                    }

                    metadata = resourceService.finishFileUpload(resource, resourceUpload, etag);
                    log.info("Multipart upload committed, bytes handled {}", resourceUpload.getContentLength());
                }

                return null;
            }
        });
        if (handler != null) {
            result.onComplete(handler);
        }
    }
Implementation of vertx {@link io.vertx.core.streams.WriteStream} that handles data chunks (from {@link io.vertx.core.streams.ReadStream}) and writes them to the blob storage.
If file content is bigger than 5MB - multipart upload will be used.
Chunk size can be configured via {@link #setWriteQueueMaxSize(int)} method, but should be no less than 5 MB according to the s3 specification.
If any exception is caught in between - multipart upload will be aborted.
server/src/main/java/com/epam/aidial/core/server/vertx/stream/BlobWriteStream.java