default CompletableFuture<String> putBlob(BlobProperties blobProperties, byte[] userMetadata,
      ReadableStreamChannel channel, PutBlobOptions options) {
    CompletableFuture<String> future = new CompletableFuture<>();
    putBlob(null, blobProperties, userMetadata, channel, options, CallbackUtils.fromCompletableFuture(future), null);
    return future;
  }
Requests for a new blob to be put asynchronously and returns a future that will eventually contain the BlobId of
the new blob on a successful response.
@param blobProperties The properties of the blob. Note that the size specified in the properties is ignored. The
                      channel is consumed fully, and the size of the blob is the number of bytes read from it.
@param userMetadata Optional user metadata about the blob. This can be null.
@param channel The {@link ReadableStreamChannel} that contains the content of the blob.
@param options The {@link PutBlobOptions} associated with the request. This cannot be null.
@return A future that would contain the BlobId eventually.
ambry-api/src/main/java/com/github/ambry/router/Router.java