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.
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
BlobServiceClient
object and provide the connection string to your storage account.To get a
BlobContainerClient
, callGetBlobContainerClient
on theBlobServiceClient
with 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
BlobServiceClient
by instantiating a newBlobServiceClientBuilder
object using the connection string to your storage account.Get a
BlobContainerClient
by calling thegetBlobContainerClient
method on theBlobServiceClient
with 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
IStorage
instance calledstorage
. There's an incomplete implementation ofIStorage
in 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 restore
This 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
using
statements 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
Initialize
method. Your app calls this method when it usesBlobStorage
for the first time. If you're curious, you can look atConfigureServices
in Startup.cs to see how call is done.Initialize
is where you want to create your container if it doesn't already exist. Replace the current implementation ofInitialize
with 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
uploadedFile
property by the JSF framework. - download: downloads an individual file by its name.
To do its work, each method uses a
Storage
instance calledstorage
. There's an incomplete implementation ofStorage
in 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
dependencyManagement
section under theproject
xml 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
dependency
to theproject/dependencies
xml 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
import
statements 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
BlobStorage
class to hold theBlobContainerClient
reference.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.
BlobStorage
is annotated with@Singleton
therefore, storing theBlobContainerClient
reference in its field is recommended.Locate the
init
method with@PostConstruct
annotation. Your app calls this method after theBlobStorage
instance is created and before it's used for the first time.init
is where to create your container if it doesn't already exist. Replace the current implementation ofinit
with 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(); } }