public CompletableFuture<Void> uploadWithResponse(String containerName, String blobName, boolean autoCreateContainer,
      InputStream data, long length, BlobHttpHeaders headers, Map<String, String> metadata, AccessTier tier,
      byte[] contentMd5, BlobRequestConditions requestConditions) {
    return FutureUtils.retry(() -> {
      BlockBlobAsyncClient blobAsyncClient = getBlockBlobAsyncClient(containerName, blobName, autoCreateContainer);
      return blobAsyncClient.uploadWithResponse(FluxUtil.toFluxByteBuffer(data), length, headers, metadata, tier,
          contentMd5, requestConditions).toFuture().thenApply(blockBlobItemResponse -> null);
    }, retries, retryPredicate, this::onRetriableError, this::onError);
  }
Creates a new block blob, or updates the content of an existing block blob asynchronously.
@param containerName name of the Azure container where the blob lives.
@param blobName name of the blob.
@param autoCreateContainer flag indicating whether to create the container if it does not exist.
@param data The data to write to the blob.
@param length The exact length of the data. It is important that this value match precisely the length of the
data provided in the {@link InputStream}.
@param headers {@link BlobHttpHeaders}
@param metadata Metadata to associate with the blob.
@param tier {@link AccessTier} for the destination blob.
@param contentMd5 An MD5 hash of the block content.
@param requestConditions {@link BlobRequestConditions}
@return a {@link CompletableFuture} of type {@link Void} that will eventually complete when block blob is uploaded
        or an exception if an error occurred.
ambry-cloud/src/main/java/com/github/ambry/cloud/azure/StorageClient.java