public void downloadBlob(CloudBlobMetadata cloudBlobMetadata, BlobId blobId, OutputStream outputStream)
      throws StoreException {
    try {
      // TODO: for GET ops, avoid extra trip to fetch metadata unless config flag is set
      // TODO: if needed, fetch metadata here and check encryption
      if (cloudBlobMetadata.getEncryptionOrigin() == EncryptionOrigin.VCR) {
        ByteBuffer encryptedBlob = ByteBuffer.allocate((int) cloudBlobMetadata.getEncryptedSize());
        requestAgent.doWithRetries(() -> {
          cloudDestination.downloadBlob(blobId, new ByteBufferOutputStream(encryptedBlob));
          return null;
        }, "Download", cloudBlobMetadata.getPartitionId());
        ByteBuffer decryptedBlob = cryptoAgent.decrypt(encryptedBlob);
        outputStream.write(decryptedBlob.array());
      } else {
        requestAgent.doWithRetries(() -> {
          cloudDestination.downloadBlob(blobId, outputStream);
          return null;
        }, "Download", cloudBlobMetadata.getPartitionId());
      }
    } catch (CloudStorageException | GeneralSecurityException | IOException e) {
      throw new StoreException("Error occurred in downloading blob for blobid :" + blobId, StoreErrorCodes.IOError);
    }
  }
Download the blob corresponding to the {@code blobId} from the {@code CloudDestination} to the given {@code outputStream}
If the blob was encrypted by vcr during upload, then this method also decrypts it.
@param cloudBlobMetadata blob metadata to determine if the blob was encrypted by vcr during upload.
@param blobId Id of the blob to the downloaded.
@param outputStream {@code OutputStream} of the downloaded blob.
@throws StoreException if there is an error in downloading the blob.
ambry-cloud/src/main/java/com/github/ambry/cloud/CloudBlobStore.java