Exercise - Configure and initialize the client library
The typical workflow for apps that use Azure Blob storage is as follows:
Retrieve configuration: At startup, load the storage account configuration, typically a storage account connection string.
Initialize client: To initialize the Azure Storage client library, use the connection string. This initialization creates the objects that the app uses to work with the Blob storage API.
Use: To operate on containers and blobs, make API calls by using the client library.
Note
This exercise is optional. If you want to complete this exercise, you'll need to create an Azure subscription before you begin. If you don't have an Azure account or you don't want to create one at this time, you can read through the instructions so you understand the information that's being presented.
Note
In this unit, you use Azure Cloud Shell as a terminal. You can access Cloud Shell through the Azure portal or the Cloud Shell sign-in. You don't have to install anything on your PC or laptop to use it.
Configure your connection string
Before you run your app, get the connection string for the storage account you use. You can use any Azure management interface to get it, including the Azure portal, the Azure CLI, and Azure PowerShell. When you set up the web app to run your code near the end of this module, use the Azure CLI to get the connection string for the storage account that you created earlier.
Storage account connection strings include the account key. Consider the account key a secret and always store it securely. Here, you store the connection string in an App Service app setting. App Service app settings are a secure place for app secrets. This design doesn't support local development and isn't a robust, end-to-end solution on its own.
Important
This code example uses a connection string to authorize access to your storage account. This configuration is for example purposes. Connection strings and account access keys should be used with caution in application code. If your account access key is lost or accidentally placed in an insecure location, your service may become vulnerable. Anyone who has the access key is able to authorize requests against the storage account, and effectively has access to all the data.
For optimal security, Microsoft recommends using managed identities for Azure resources to authorize requests against blob, queue, and table data, whenever possible. To learn more, see Authorize access to blobs using Microsoft Entra ID.
Initialize the Blob storage object model
In the Azure Storage SDK for .NET, the standard pattern for using Blob storage is as follows:
Instantiate a new
BlobServiceClientobject and provide the connection string to your storage account.To get a
BlobContainerClient, callGetBlobContainerClienton theBlobServiceClientwith the name of the container you want to interact with or create.
In code, these steps look like this.
BlobServiceClient blobServiceClient = new BlobServiceClient(storageConfig.ConnectionString);
BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(storageConfig.FileContainerName);
None of this initialization code makes calls over the network. This fact means that some exceptions that occur because of incorrect information aren't thrown until later. For example, if an incorrectly formatted connection string is supplied to the constructor of the BlobServiceClient class, an exception is thrown immediately. However, if the connection string points to a storage account that doesn't exist, no exception is thrown until you attempt an operation against the storage account.
In the Azure Storage SDK for Java, the standard pattern for using Blob Storage consists of the following steps:
Build a
BlobServiceClientby instantiating a newBlobServiceClientBuilderobject using the connection string to your storage account.Get a
BlobContainerClientby calling thegetBlobContainerClientmethod on theBlobServiceClientwith the name of the container you want to interact with or create.
In code, these steps look like this.
BlobServiceClient blobServiceClient = BlobServiceClientBuilder()
.connectionString(connectionString)
.buildClient();
BlobContainerClient containerClient = blobServiceClient.getBlobContainerClient(containerName);
None of this initialization code makes calls over the network. This fact means that some exceptions that occur because of incorrect information aren't thrown until later. For example, if an incorrectly formatted connection string is supplied to the BlobServiceClientBuilder, an exception is thrown immediately. However, if the connection string points to a storage account that doesn't exist, no exception is thrown until you attempt an operation against the storage account.
Create containers at startup
To create a container when your app starts or when the app first tries to use a container, call CreateIfNotExistsAsync on a BlobContainerClient.
CreateIfNotExistsAsync doesn't throw an exception if the container already exists, but it does make a network call to Azure Blob Storage. Call it once during initialization, not every time you try to use a container.
To create a container when your app starts or when it first tries to use it, call exists on a BlobContainerClient to check whether a container already exists. If it doesn't exist, then call create. Call it once during initialization, not every time you try to use a container.
Exercise
Clone and explore the unfinished app
First, clone the starter app from GitHub. To get a copy of the source code and open it in the editor, run the following commands in Azure Shell CLI:
git clone https://github.com/MicrosoftDocs/mslearn-store-data-in-azure.git cd mslearn-store-data-in-azure/store-app-data-with-azure-blob-storage/src/start code .In the editor, open the file Controllers/FilesController.cs. There's no work to do here, but have a quick look at what the app does.
This controller implements an API with three actions:
- Index: (
GET /api/Files) returns a list of URLs, one for each file that's been uploaded. The app front end calls this method to build a list of hyperlinks to the uploaded files. - Upload: (
POST /api/Files) receives an uploaded file and saves it. - Download: (
GET /api/Files/{filename}) downloads an individual file by its name.
To do its work, each method uses an
IStorageinstance calledstorage. There's an incomplete implementation ofIStoragein Models/BlobStorage.cs to fill in.- Index: (
Add the NuGet package
Add a reference to the Azure Storage SDK. Run the following commands in Azure Shell CLI:
dotnet add package Azure.Storage.Blobs dotnet restoreThis command ensures you're using the newest version of the Blob Storage client library.
Configure
The configuration values you need are the storage-account connection string and the name of the container the app uses to store files. In this module, you're only going to run the app in Azure App Service. Follow App Service best practice and store the values in App Service app settings. You do that when you create the App Service instance. There's nothing you need to do at the moment.
When it comes to using the configuration, the starter app includes the plumbing you need. The IOptions<AzureStorageConfig> constructor parameter in BlobStorage has two properties: the storage-account connection string and the name of the container your app uses to store blobs. There's code in the ConfigureServices method of Startup.cs that loads the values from configuration when the app starts.
Initialize
In the editor, open Models/BlobStorage.cs. At the top of the file, add the following
usingstatements to prepare it for the code that you're going to add.using Azure; using Azure.Storage.Blobs; using Azure.Storage.Blobs.Models;Locate the
Initializemethod. Your app calls this method when it usesBlobStoragefor the first time. If you're curious, you can look atConfigureServicesin Startup.cs to see how call is done.Initializeis where you want to create your container if it doesn't already exist. Replace the current implementation ofInitializewith the following code, and save your work using CTRL+S.public Task Initialize() { BlobServiceClient blobServiceClient = new BlobServiceClient(storageConfig.ConnectionString); BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(storageConfig.FileContainerName); return containerClient.CreateIfNotExistsAsync(); }
Clone and explore the unfinished app
First, clone the starter app from GitHub. To get a copy of the source code and open it in the editor, run the following commands in Azure Shell CLI:
git clone https://github.com/MicrosoftDocs/mslearn-store-data-in-azure.git cd mslearn-store-data-in-azure/store-java-ee-application-data-with-azure-blob-storage/start code .In the editor, open the file src/main/java/com/microsoft/azure/samples/jsf/IndexBean.java. There's no work to do here, but have a quick look at what the app does.
This request scoped bean implements three actions that are used by src/main/webapp/index.xhtml Java Server Faces (JSF) page:
- listFileNames: returns a list of file names, one for each file that's been uploaded. The index.xhtml page calls this method to build a list of hyperlinks to the uploaded files.
- upload: receives an uploaded file and saves it. The file content and metadata is injected into the
uploadedFileproperty by the JSF framework. - download: downloads an individual file by its name.
To do its work, each method uses a
Storageinstance calledstorage. There's an incomplete implementation ofStoragein src/main/java/com/microsoft/azure/samples/service/BlobStorage.java to fill in.
Add the Azure Storage SDK for Java reference
We recommend using the Azure BOM to add Azure client libraries to the project. It provides a simple and elegant way to orchestrate using multiple Azure client libraries while ensuring minimal dependency conflicts.
In the editor, open the file pom.xml.
To add Azure BOM to the project, add the following
dependencyManagementsection under theprojectxml tag.<dependencyManagement> <dependencies> <dependency> <groupId>com.azure</groupId> <artifactId>azure-sdk-bom</artifactId> <version>1.0.6</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>To add Azure Storage SDK for Java, add the following
dependencyto theproject/dependenciesxml section.<dependency> <groupId>com.azure</groupId> <artifactId>azure-storage-blob</artifactId> </dependency>
Configure
The configuration values you need are the storage account connection string and the name of the container the app uses to store files. In this module, you're only going to run the app in Azure App Service. Follow App Service best practice and store the values in App Service app settings. You do that when we create the App Service instance. There's nothing you need to do at the moment.
When it comes to using the configuration, the App Service app settings are passed as environment variables to the app code. You read them in the initialization code.
Initialize
In the editor, open src/main/java/com/microsoft/azure/samples/service/BlobStorage.java. At the top of the file, add the following
importstatements to prepare it for the code you're going to add.import java.util.stream.Collectors; import com.azure.storage.blob.BlobClient; import com.azure.storage.blob.BlobContainerClient; import com.azure.storage.blob.BlobServiceClient; import com.azure.storage.blob.BlobServiceClientBuilder; import com.azure.storage.blob.models.BlobItem;Add a class property in the
BlobStorageclass to hold theBlobContainerClientreference.private BlobContainerClient blobContainerClient;Tip
Azure clients are stateless and thread-safe. It is recommended to cache their instances where applicable. For example, the app you're working on uses a single container with a constant name, therefore it's best to cache it in app lifetime scope.
BlobStorageis annotated with@Singletontherefore, storing theBlobContainerClientreference in its field is recommended.Locate the
initmethod with@PostConstructannotation. Your app calls this method after theBlobStorageinstance is created and before it's used for the first time.initis where to create your container if it doesn't already exist. Replace the current implementation ofinitwith the following code, and save your work.@PostConstruct private void init() { String connectionString = System.getenv("STORAGE_CONNECTION_STRING"); String containerName = System.getenv("STORAGE_CONTAINER_NAME"); BlobServiceClient blobServiceClient = new BlobServiceClientBuilder() .connectionString(connectionString) .buildClient(); blobContainerClient = blobServiceClient.getBlobContainerClient(containerName); if (!blobContainerClient.exists()) { blobContainerClient.create(); } }