public CompletableFuture<Boolean> downloadFileAsync(String containerName, String fileName, BlobId blobId,
      OutputStream outputStream, boolean errorOnNotFound) {
    return FutureUtils.orTimeout(
        storageClient.downloadWithResponse(containerName, fileName, blobId, false, outputStream, null, null,
            defaultRequestConditions, false), uploadTimeout).handle((unused, throwable) -> {
      if (throwable != null) {
        Exception ex = Utils.extractFutureExceptionCause(throwable);
        if (!errorOnNotFound && ex instanceof BlobStorageException && isNotFoundError(
            ((BlobStorageException) ex).getErrorCode())) {
          // Received exception for blob not found.
          return false;
        }
        throw throwable instanceof CompletionException ? (CompletionException) throwable
            : new CompletionException(throwable);
      }
      // Blob downloaded successfully
      return true;
    });
  }
Download a file from blob storage asynchronously.
@param containerName name of the container containing blob to download.
@param fileName name of the blob.
@param blobId Ambry {@link BlobId} associated with the {@code fileName}.
@param outputStream the output stream to use for download.
@param errorOnNotFound If {@code true}, throw BlobStorageException on blob not found, otherwise return false.
@return a CompletableFuture of type {@link Boolean} that will eventually be {@code true} if the download was successful,
        or {@code false} if the blob was not found.  If there was an error on ABS side, it will complete exceptionally
        containing the BlobStorageException.
ambry-cloud/src/main/java/com/github/ambry/cloud/azure/AzureBlobDataAccessor.java