# SkillPatch skill: azure-storage-blob-java

This skill guides agents in building Azure Blob Storage applications using the Azure Storage Blob SDK for Java. It covers client creation (BlobServiceClient, BlobContainerClient, BlobClient), authentication methods (SAS token, connection string, DefaultAzureCredential), and core operations such as creating containers, uploading data from strings, files, and streams.

You (the agent) were given this URL and asked to use this skill. This is a **public** skill — no SkillPatch account, API key, or prior setup is required. Two ways to use it:

**1. Use it right now** — the skill's complete file tree (SKILL.md + all reference files) is inlined below; read `SKILL.md` first, then follow it, consulting the other files as it directs.

**2. Install the exact package onto disk** (recommended if you can run a shell — this reproduces the skill byte-for-byte, including any binary assets that can't be inlined):

```bash
mkdir -p .claude/skills/azure-storage-blob-java
curl -sSL https://skillpatch.dev/install_skill/azure-storage-blob-java | tar -xz -C .claude/skills/
```

(`.claude/skills/` is Claude Code's convention; use whatever directory your agent loads skills from.)


---

## Skill files (2)

- `SKILL.md`
- `references/examples.md`


### `SKILL.md`

````markdown
---
name: azure-storage-blob-java
description: Build blob storage applications with Azure Storage Blob SDK for Java. Use when uploading, downloading, or managing files in Azure Blob Storage, working with containers, or implementing streaming data operations.
license: MIT
metadata:
  author: Microsoft
  version: "1.0.0"
  package: com.azure:azure-storage-blob
---

# Azure Storage Blob SDK for Java

Build blob storage applications using the Azure Storage Blob SDK for Java.

## Installation

```xml
<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-storage-blob</artifactId>
    <version>12.33.0</version>
</dependency>
```

## Client Creation

### BlobServiceClient

```java
import com.azure.storage.blob.BlobServiceClient;
import com.azure.storage.blob.BlobServiceClientBuilder;

// With SAS token
BlobServiceClient serviceClient = new BlobServiceClientBuilder()
    .endpoint("<storage-account-url>")
    .sasToken("<sas-token>")
    .buildClient();

// With connection string
BlobServiceClient serviceClient = new BlobServiceClientBuilder()
    .connectionString("<connection-string>")
    .buildClient();
```

### With DefaultAzureCredential

```java
import com.azure.core.credential.TokenCredential;
import com.azure.identity.AzureIdentityEnvVars;
import com.azure.identity.DefaultAzureCredentialBuilder;
import com.azure.identity.ManagedIdentityCredentialBuilder;

// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
TokenCredential credential = new DefaultAzureCredentialBuilder()
    .requireEnvVars(AzureIdentityEnvVars.AZURE_TOKEN_CREDENTIALS)
    .build();
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/java/api/overview/azure/identity-readme?view=azure-java-stable#credential-classes
// TokenCredential credential = new ManagedIdentityCredentialBuilder().build();

BlobServiceClient serviceClient = new BlobServiceClientBuilder()
    .endpoint("<storage-account-url>")
    .credential(credential)
    .buildClient();
```

### BlobContainerClient

```java
import com.azure.storage.blob.BlobContainerClient;

// From service client
BlobContainerClient containerClient = serviceClient.getBlobContainerClient("mycontainer");

// Direct construction
BlobContainerClient containerClient = new BlobContainerClientBuilder()
    .connectionString("<connection-string>")
    .containerName("mycontainer")
    .buildClient();
```

### BlobClient

```java
import com.azure.storage.blob.BlobClient;

// From container client
BlobClient blobClient = containerClient.getBlobClient("myblob.txt");

// With directory structure
BlobClient blobClient = containerClient.getBlobClient("folder/subfolder/myblob.txt");

// Direct construction
BlobClient blobClient = new BlobClientBuilder()
    .connectionString("<connection-string>")
    .containerName("mycontainer")
    .blobName("myblob.txt")
    .buildClient();
```

## Core Patterns

### Create Container

```java
// Create container
serviceClient.createBlobContainer("mycontainer");

// Create if not exists
BlobContainerClient container = serviceClient.createBlobContainerIfNotExists("mycontainer");

// From container client
containerClient.create();
containerClient.createIfNotExists();
```

### Upload Data

```java
import com.azure.core.util.BinaryData;

// Upload string
String data = "Hello, Azure Blob Storage!";
blobClient.upload(BinaryData.fromString(data));

// Upload with overwrite
blobClient.upload(BinaryData.fromString(data), true);
```

### Upload from File

```java
blobClient.uploadFromFile("local-file.txt");

// With overwrite
blobClient.uploadFromFile("local-file.txt", true);
```

### Upload from Stream

```java
import com.azure.storage.blob.specialized.BlockBlobClient;

BlockBlobClient blockBlobClient = blobClient.getBlockBlobClient();

try (ByteArrayInputStream dataStream = new ByteArrayInputStream(data.getBytes())) {
    blockBlobClient.upload(dataStream, data.length());
}
```

### Upload with Options

```java
import com.azure.storage.blob.models.BlobHttpHeaders;
import com.azure.storage.blob.options.BlobParallelUploadOptions;

BlobHttpHeaders headers = new BlobHttpHeaders()
    .setContentType("text/plain")
    .setCacheControl("max-age=3600");

Map<String, String> metadata = Map.of("author", "john", "version", "1.0");

try (InputStream stream = new FileInputStream("large-file.bin")) {
    BlobParallelUploadOptions options = new BlobParallelUploadOptions(stream)
        .setHeaders(headers)
        .setMetadata(metadata);
    
    blobClient.uploadWithResponse(options, null, Context.NONE);
}
```

### Upload if Not Exists

```java
import com.azure.storage.blob.models.BlobRequestConditions;

BlobParallelUploadOptions options = new BlobParallelUploadOptions(inputStream, length)
    .setRequestConditions(new BlobRequestConditions().setIfNoneMatch("*"));

blobClient.uploadWithResponse(options, null, Context.NONE);
```

### Download Data

```java
// Download to BinaryData
BinaryData content = blobClient.downloadContent();
String text = content.toString();

// Download to file
blobClient.downloadToFile("downloaded-file.txt");
```

### Download to Stream

```java
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
    blobClient.downloadStream(outputStream);
    byte[] data = outputStream.toByteArray();
}
```

### Download with InputStream

```java
import com.azure.storage.blob.specialized.BlobInputStream;

try (BlobInputStream blobIS = blobClient.openInputStream()) {
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = blobIS.read(buffer)) != -1) {
        // Process buffer
    }
}
```

### Upload via OutputStream

```java
import com.azure.storage.blob.specialized.BlobOutputStream;

try (BlobOutputStream blobOS = blobClient.getBlockBlobClient().getBlobOutputStream()) {
    blobOS.write("Data to upload".getBytes());
}
```

### List Blobs

```java
import com.azure.storage.blob.models.BlobItem;

// List all blobs
for (BlobItem blobItem : containerClient.listBlobs()) {
    System.out.println("Blob: " + blobItem.getName());
}

// List with prefix (virtual directory)
import com.azure.storage.blob.models.ListBlobsOptions;

ListBlobsOptions options = new ListBlobsOptions().setPrefix("folder/");
for (BlobItem blobItem : containerClient.listBlobs(options, null)) {
    System.out.println("Blob: " + blobItem.getName());
}
```

### List Blobs by Hierarchy

```java
import com.azure.storage.blob.models.BlobListDetails;

String delimiter = "/";
ListBlobsOptions options = new ListBlobsOptions()
    .setPrefix("data/")
    .setDetails(new BlobListDetails().setRetrieveMetadata(true));

for (BlobItem item : containerClient.listBlobsByHierarchy(delimiter, options, null)) {
    if (item.isPrefix()) {
        System.out.println("Directory: " + item.getName());
    } else {
        System.out.println("Blob: " + item.getName());
    }
}
```

### Delete Blob

```java
blobClient.delete();

// Delete if exists
blobClient.deleteIfExists();

// Delete with snapshots
import com.azure.storage.blob.models.DeleteSnapshotsOptionType;
blobClient.deleteWithResponse(DeleteSnapshotsOptionType.INCLUDE, null, null, Context.NONE);
```

### Copy Blob

```java
import com.azure.storage.blob.models.BlobCopyInfo;
import com.azure.core.util.polling.SyncPoller;

// Async copy (for large blobs or cross-account)
SyncPoller<BlobCopyInfo, Void> poller = blobClient.beginCopy("<source-blob-url>", Duration.ofSeconds(1));
poller.waitForCompletion();

// Sync copy from URL (for same account)
blobClient.copyFromUrl("<source-blob-url>");
```

### Generate SAS Token

```java
import com.azure.storage.blob.sas.*;
import java.time.OffsetDateTime;

// Blob-level SAS
BlobSasPermission permissions = new BlobSasPermission().setReadPermission(true);
OffsetDateTime expiry = OffsetDateTime.now().plusDays(1);

BlobServiceSasSignatureValues sasValues = new BlobServiceSasSignatureValues(expiry, permissions);
String sasToken = blobClient.generateSas(sasValues);

// Container-level SAS
BlobContainerSasPermission containerPermissions = new BlobContainerSasPermission()
    .setReadPermission(true)
    .setListPermission(true);
    
BlobServiceSasSignatureValues containerSasValues = new BlobServiceSasSignatureValues(expiry, containerPermissions);
String containerSas = containerClient.generateSas(containerSasValues);
```

### Blob Properties and Metadata

```java
import com.azure.storage.blob.models.BlobProperties;

// Get properties
BlobProperties properties = blobClient.getProperties();
System.out.println("Size: " + properties.getBlobSize());
System.out.println("Content-Type: " + properties.getContentType());
System.out.println("Last Modified: " + properties.getLastModified());

// Set metadata
Map<String, String> metadata = Map.of("key1", "value1", "key2", "value2");
blobClient.setMetadata(metadata);

// Set HTTP headers
BlobHttpHeaders headers = new BlobHttpHeaders()
    .setContentType("application/json")
    .setCacheControl("max-age=86400");
blobClient.setHttpHeaders(headers);
```

### Lease Blob

```java
import com.azure.storage.blob.specialized.BlobLeaseClient;
import com.azure.storage.blob.specialized.BlobLeaseClientBuilder;

BlobLeaseClient leaseClient = new BlobLeaseClientBuilder()
    .blobClient(blobClient)
    .buildClient();

// Acquire lease (-1 for infinite)
String leaseId = leaseClient.acquireLease(60);

// Renew lease
leaseClient.renewLease();

// Release lease
leaseClient.releaseLease();
```

## Error Handling

```java
import com.azure.storage.blob.models.BlobStorageException;

try {
    blobClient.download(outputStream);
} catch (BlobStorageException e) {
    System.out.println("Status: " + e.getStatusCode());
    System.out.println("Error code: " + e.getErrorCode());
    // 404 = Blob not found
    // 409 = Conflict (lease, etc.)
}
```

## Proxy Configuration

```java
import com.azure.core.http.ProxyOptions;
import com.azure.core.http.netty.NettyAsyncHttpClientBuilder;
import java.net.InetSocketAddress;

ProxyOptions proxyOptions = new ProxyOptions(
    ProxyOptions.Type.HTTP,
    new InetSocketAddress("localhost", 8888));

BlobServiceClient client = new BlobServiceClientBuilder()
    .endpoint("<endpoint>")
    .sasToken("<sas-token>")
    .httpClient(new NettyAsyncHttpClientBuilder().proxy(proxyOptions).build())
    .buildClient();
```

## Environment Variables

```bash
AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=...  # Alternative to Entra ID auth
AZURE_STORAGE_ACCOUNT_URL=https://<account>.blob.core.windows.net  # Required for all auth methods
AZURE_TOKEN_CREDENTIALS=prod  # Required only if DefaultAzureCredential is used in production
```

## Trigger Phrases

- "Azure Blob Storage Java"
- "upload download blob"
- "blob container SDK"
- "storage streaming"
- "SAS token generation"
- "blob metadata properties"

````


### `references/examples.md`

````markdown
# Azure Storage Blob Java SDK - Examples

Comprehensive code examples for the Azure Storage Blob SDK for Java.

## Table of Contents

- [Maven Dependency](#maven-dependency)
- [Client Creation](#client-creation)
- [Container Operations](#container-operations)
- [Upload Blobs](#upload-blobs)
- [Download Blobs](#download-blobs)
- [List Blobs](#list-blobs)
- [SAS Token Generation](#sas-token-generation)
- [Error Handling](#error-handling)

---

## Maven Dependency

```xml
<dependency>
    <groupId>com.azure</groupId>
    <artifactId>azure-storage-blob</artifactId>
    <version>12.33.0</version>
</dependency>
```

Or use the BOM:

```xml
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.azure</groupId>
            <artifactId>azure-sdk-bom</artifactId>
            <version>{bom_version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>com.azure</groupId>
        <artifactId>azure-storage-blob</artifactId>
    </dependency>
</dependencies>
```

---

## Client Creation

### Using Shared Key Credential

```java
import com.azure.storage.blob.BlobServiceClient;
import com.azure.storage.blob.BlobServiceClientBuilder;
import com.azure.storage.common.StorageSharedKeyCredential;

StorageSharedKeyCredential credential = new StorageSharedKeyCredential(accountName, accountKey);

String endpoint = String.format("https://%s.blob.core.windows.net", accountName);
BlobServiceClient blobServiceClient = new BlobServiceClientBuilder()
    .endpoint(endpoint)
    .credential(credential)
    .buildClient();
```

### Using SAS Token

```java
// Separate endpoint and SAS token
BlobServiceClient blobServiceClient = new BlobServiceClientBuilder()
    .endpoint("<your-storage-account-url>")
    .sasToken("<your-sasToken>")
    .buildClient();

// Combined URL with SAS token
BlobServiceClient blobServiceClient = new BlobServiceClientBuilder()
    .endpoint("<your-storage-account-url>" + "?" + "<your-sasToken>")
    .buildClient();
```

### Using DefaultAzureCredential (Recommended)

```java
import com.azure.identity.DefaultAzureCredentialBuilder;

BlobServiceClient blobServiceClient = new BlobServiceClientBuilder()
    .endpoint("<your-storage-account-url>")
    .credential(new DefaultAzureCredentialBuilder().build())
    .buildClient();
```

---

## Container Operations

### Create BlobContainerClient

```java
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobContainerClientBuilder;

// From BlobServiceClient
BlobContainerClient containerClient = blobServiceClient.getBlobContainerClient("mycontainer");

// From builder with SAS token
BlobContainerClient containerClient = new BlobContainerClientBuilder()
    .endpoint("<your-storage-account-url>")
    .sasToken("<your-sasToken>")
    .containerName("mycontainer")
    .buildClient();
```

### Create Container

```java
import java.util.Collections;
import java.util.Map;

// Simple create
containerClient.create();

// Create with metadata
Map<String, String> metadata = Collections.singletonMap("mykey", "myvalue");
containerClient.createWithResponse(metadata, null, null, Context.NONE);

// Create from service client
blobServiceClient.createBlobContainer("mycontainer");
```

### List and Delete Containers

```java
// List all containers
blobServiceClient.listBlobContainers().forEach(containerItem -> {
    System.out.println("Container name: " + containerItem.getName());
});

// Delete container
containerClient.delete();
```

---

## Upload Blobs

### Upload BinaryData

```java
import com.azure.core.util.BinaryData;
import com.azure.storage.blob.BlobClient;

BlobClient blobClient = containerClient.getBlobClient("myblob");
String dataSample = "samples";
blobClient.upload(BinaryData.fromString(dataSample));
```

### Upload from InputStream

```java
import com.azure.storage.blob.specialized.BlockBlobClient;
import java.io.ByteArrayInputStream;

BlockBlobClient blockBlobClient = containerClient.getBlobClient("myblockblob").getBlockBlobClient();
String dataSample = "samples";
try (ByteArrayInputStream dataStream = new ByteArrayInputStream(dataSample.getBytes())) {
    blockBlobClient.upload(dataStream, dataSample.length());
}
```

### Upload from File

```java
BlobClient blobClient = containerClient.getBlobClient("myblockblob");
blobClient.uploadFromFile("local-file.jpg");
```

### Upload with Overwrite Control

```java
// Upload without overwrite (fails if blob exists)
blobClient.upload(BinaryData.fromString("data"), false);

// Upload with overwrite enabled
blobClient.upload(BinaryData.fromString("data"), true);

// Upload with access conditions (fail if blob exists)
import com.azure.storage.blob.models.BlobRequestConditions;
import com.azure.storage.blob.options.BlobParallelUploadOptions;

BlobParallelUploadOptions options = new BlobParallelUploadOptions(dataStream, dataSample.length());
options.setRequestConditions(new BlobRequestConditions().setIfNoneMatch("*"));
blobClient.uploadWithResponse(options, null, Context.NONE);
```

### Upload with Metadata and Headers

```java
import com.azure.storage.blob.models.BlobHttpHeaders;
import java.security.MessageDigest;
import java.nio.charset.StandardCharsets;

Map<String, String> metadata = Collections.singletonMap("myblobmetadata", "sample");
BlobHttpHeaders headers = new BlobHttpHeaders()
    .setContentDisposition("attachment")
    .setContentType("text/html; charset=utf-8");

String data = "Hello world!";
byte[] md5 = MessageDigest.getInstance("MD5").digest(data.getBytes(StandardCharsets.UTF_8));

try (InputStream dataStream = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8))) {
    blockBlobClient.uploadWithResponse(
        dataStream, 
        data.length(), 
        headers, 
        metadata, 
        null,  // tier
        md5,   // content MD5
        null,  // request conditions
        null,  // timeout
        null   // context
    );
}
```

---

## Download Blobs

### Download to BinaryData

```java
BinaryData content = blobClient.downloadContent();
String text = content.toString();
```

### Download to OutputStream

```java
import java.io.ByteArrayOutputStream;

try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
    blobClient.downloadStream(outputStream);
    byte[] data = outputStream.toByteArray();
}
```

### Download to File

```java
blobClient.downloadToFile("downloaded-file.jpg");
```

### Download via InputStream

```java
import com.azure.storage.blob.specialized.BlobInputStream;

try (BlobInputStream blobIS = blobClient.openInputStream()) {
    int data = blobIS.read();
    // Process data...
}
```

---

## List Blobs

### Simple Listing

```java
import com.azure.storage.blob.models.BlobItem;

for (BlobItem blobItem : containerClient.listBlobs()) {
    System.out.println("Blob name: " + blobItem.getName());
}
```

### List and Create Clients

```java
for (BlobItem blobItem : containerClient.listBlobs()) {
    BlobClient blobClient;
    if (blobItem.getSnapshot() != null) {
        blobClient = containerClient.getBlobClient(blobItem.getName(), blobItem.getSnapshot());
    } else {
        blobClient = containerClient.getBlobClient(blobItem.getName());
    }
    System.out.println("Blob URI: " + blobClient.getBlobUrl());
}
```

---

## SAS Token Generation

```java
import com.azure.storage.blob.sas.*;
import com.azure.storage.common.sas.*;
import java.time.OffsetDateTime;

// Generate Account SAS
OffsetDateTime expiryTime = OffsetDateTime.now().plusDays(1);
AccountSasPermission accountSasPermission = new AccountSasPermission().setReadPermission(true);
AccountSasService services = new AccountSasService().setBlobAccess(true);
AccountSasResourceType resourceTypes = new AccountSasResourceType().setObject(true);

AccountSasSignatureValues accountSasValues = new AccountSasSignatureValues(
    expiryTime, accountSasPermission, services, resourceTypes);
String sasToken = blobServiceClient.generateAccountSas(accountSasValues);

// Generate Container SAS
BlobContainerSasPermission containerSasPermission = new BlobContainerSasPermission().setCreatePermission(true);
BlobServiceSasSignatureValues serviceSasValues = new BlobServiceSasSignatureValues(expiryTime, containerSasPermission);
String containerSas = containerClient.generateSas(serviceSasValues);

// Generate Blob SAS
BlobSasPermission blobSasPermission = new BlobSasPermission().setReadPermission(true);
BlobServiceSasSignatureValues blobSasValues = new BlobServiceSasSignatureValues(expiryTime, blobSasPermission);
String blobSas = blobClient.generateSas(blobSasValues);
```

---

## Error Handling

```java
import com.azure.storage.blob.models.BlobErrorCode;
import com.azure.storage.blob.models.BlobStorageException;

try {
    containerClient.create();
} catch (BlobStorageException e) {
    if (e.getErrorCode() == BlobErrorCode.CONTAINER_ALREADY_EXISTS) {
        System.out.println("Container already exists: " + containerClient.getBlobContainerUrl());
    } else if (e.getErrorCode() == BlobErrorCode.CONTAINER_BEING_DELETED) {
        System.out.println("Container is being deleted: " + e.getServiceMessage());
    } else if (e.getErrorCode() == BlobErrorCode.RESOURCE_NOT_FOUND) {
        System.out.println("Resource not found, status: " + e.getStatusCode());
    } else {
        throw e;
    }
}
```

---

## Complete Working Example

```java
import com.azure.storage.blob.*;
import com.azure.storage.blob.specialized.BlockBlobClient;
import com.azure.storage.common.StorageSharedKeyCredential;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Locale;

public class BasicExample {

    public static void main(String[] args) throws IOException {
        String accountName = System.getenv("AZURE_STORAGE_ACCOUNT_NAME");
        String accountKey = System.getenv("AZURE_STORAGE_ACCOUNT_KEY");

        StorageSharedKeyCredential credential = new StorageSharedKeyCredential(accountName, accountKey);
        String endpoint = String.format(Locale.ROOT, "https://%s.blob.core.windows.net", accountName);

        BlobServiceClient storageClient = new BlobServiceClientBuilder()
            .endpoint(endpoint)
            .credential(credential)
            .buildClient();

        // Create container (names must be lowercase)
        BlobContainerClient containerClient = storageClient
            .getBlobContainerClient("myjavacontainer" + System.currentTimeMillis());
        containerClient.create();

        // Create blob client
        BlockBlobClient blobClient = containerClient
            .getBlobClient("HelloWorld.txt")
            .getBlockBlobClient();

        // Upload data
        String data = "Hello world!";
        try (InputStream dataStream = new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8))) {
            blobClient.upload(dataStream, data.length());
        }

        // Download data
        int dataSize = (int) blobClient.getProperties().getBlobSize();
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(dataSize)) {
            blobClient.downloadStream(outputStream);
            String downloaded = new String(outputStream.toByteArray(), StandardCharsets.UTF_8);
            System.out.println("Downloaded: " + downloaded);
        }

        // List blobs
        containerClient.listBlobs().forEach(blobItem -> 
            System.out.println("Blob: " + blobItem.getName()));

        // Cleanup
        blobClient.delete();
        containerClient.delete();
    }
}
```

````
