# Azure Storage Blob - Producer Operations

[Back to Azure Storage Blob Component](../azure-storage-blob-component.md)

## Common Pattern

Most producer operations follow the same pattern: set the `operation` parameter on the endpoint URI, optionally override the container or blob name via headers, and send the exchange.

-   Java
    
-   XML
    
-   YAML
    

```java
from("direct:start")
    .to("azure-storage-blob://camelazure/container1?blobName=hello.txt&operation=getBlob&serviceClient=#client")
    .to("mock:result");
```

```xml
<route>
  <from uri="direct:start"/>
  <to uri="azure-storage-blob://camelazure/container1?blobName=hello.txt&amp;operation=getBlob&amp;serviceClient=#client"/>
  <to uri="mock:result"/>
</route>
```

```yaml
- route:
    from:
      uri: direct:start
      steps:
        - to:
            uri: azure-storage-blob://camelazure/container1
            parameters:
              blobName: hello.txt
              operation: getBlob
              serviceClient: "#client"
        - to:
            uri: mock:result
```

You can override the container name or blob name at runtime using the `CamelAzureStorageBlobBlobContainerName` and `CamelAzureStorageBlobBlobName` headers respectively.

## Operations Reference

The following table lists all available producer operations. Operations marked **Java-only** require programmatic objects (PageRange, BlobBlock, etc.) that cannot be expressed in XML or YAML.

  
| Operation | Description | Notes |
| --- | --- | --- |
| `listBlobContainers` | List all blob containers in the storage account. | Java-only (requires `ListBlobContainersOptions`) |
| `createBlobContainer` | Create a new blob container. |  |
| `deleteBlobContainer` | Delete a blob container. |  |
| `listBlobs` | List blobs in a container. |  |
| `listBlobVersions` | List all versions of blobs in a container. Versioning must be enabled on the storage account. | Use `CamelAzureStorageBlobPrefix` header to filter by blob name |
| `getBlob` | Download a blob. Returns an `InputStream`, or writes to an `OutputStream` if one is set in the body. | See [getBlob](#getBlob) |
| `deleteBlob` | Delete a blob. |  |
| `downloadBlobToFile` | Download a blob directly to a file. Use the `fileDir` option to set the target directory. |  |
| `downloadLink` | Generate a download link for a blob. The link is returned in the `CamelAzureStorageBlobDownloadLink` header. |  |
| `uploadBlockBlob` | Upload a block blob. The body is used as the blob content. |  |
| `uploadBlockBlobChunked` | Upload a large file using chunked parallel uploads. | See [uploadBlockBlobChunked](#uploadBlockBlobChunked) |
| `stageBlockBlobList` | Stage a list of blocks for later commit. | Java-only (requires `List<BlobBlock>` body) |
| `commitBlobBlockList` | Commit a list of previously staged blocks. | Java-only (requires `List<Block>` body) |
| `getBlobBlockList` | Get the list of committed and uncommitted blocks for a block blob. |  |
| `createAppendBlob` | Create an append blob. |  |
| `commitAppendBlob` | Append content to an append blob. The body is used as the content to append. |  |
| `createPageBlob` | Create a page blob. |  |
| `uploadPageBlob` | Upload data to a page blob at the range specified by the `CamelAzureStorageBlobPageBlobRange` header. | Java-only (requires `PageRange` header) |
| `resizePageBlob` | Resize a page blob. | Java-only (requires `PageRange` header) |
| `clearPageBlob` | Clear a range of pages in a page blob. | Java-only (requires `PageRange` header) |
| `getPageBlobRanges` | Get the page ranges for a page blob. | Java-only (requires `PageRange` header) |
| `copyBlob` | Copy a blob from one container to another. | See [copyBlob](#copyBlob) |
| `createBlobSnapshot` | Create a snapshot of a blob. The snapshot ID is returned in the `CamelAzureStorageBlobSnapshotId` header. |  |
| `setBlobTags` | Set index tags on a blob. | Java-only (requires `Map<String, String>` via `CamelAzureStorageBlobTags` header) |
| `getBlobTags` | Get the index tags of a blob. |  |
| `findBlobsByTags` | Find blobs across containers by tag filter expression. | See [findBlobsByTags](#findBlobsByTags) |
| `setBlobLegalHold` | Place or clear a legal hold on a blob. | See [setBlobLegalHold](#setBlobLegalHold) |
| `setBlobImmutabilityPolicy` | Set an immutability policy on a blob. | Java-only (requires `OffsetDateTime` and `BlobImmutabilityPolicyMode`) |
| `setBlobTier` | Change the access tier of a blob (Hot, Cool, Cold, Archive). | See [setBlobTier](#setBlobTier) |
| `undeleteBlob` | Restore a soft-deleted blob. |  |

## Operation Details

This section covers operations that require special configuration beyond the common pattern.

### getBlob

You can either set an `OutputStream` in the exchange body and write the data to it:

_Java-only: programmatic OutputStream handling_

```java
from("direct:start")
    .process(exchange -> {
        exchange.getIn().setHeader("CamelAzureStorageBlobBlobContainerName", "overridenName");
        exchange.getIn().setBody(outputStream);
    })
    .to("azure-storage-blob://camelazure/container1?blobName=blob&operation=getBlob&serviceClient=#client")
    .to("mock:result");
```

If you don’t set a body, then this operation will give you an `InputStream` instance which can be processed further downstream.

### uploadBlockBlobChunked

This operation is recommended for uploading large files (larger than 256MB) as it uses chunked parallel uploads for memory efficiency.

-   Java
    
-   XML
    
-   YAML
    

```java
from("file://data?noop=true")
    .log("Uploading file: ${header.CamelFileName}")
    .toD("azure-storage-blob://camelazure/container1?blobName=${header.CamelFileName}&operation=uploadBlockBlobChunked&blockSize=52428800&maxConcurrency=4&serviceClient=#client")
    .log("Upload completed: ${header.CamelFileName}");
```

```xml
<route>
  <from uri="file://data?noop=true"/>
  <log message="Uploading file: ${header.CamelFileName}"/>
  <toD uri="azure-storage-blob://camelazure/container1?blobName=${header.CamelFileName}&amp;operation=uploadBlockBlobChunked&amp;blockSize=52428800&amp;maxConcurrency=4&amp;serviceClient=#client"/>
  <log message="Upload completed: ${header.CamelFileName}"/>
</route>
```

```yaml
- route:
    from:
      uri: file://data
      parameters:
        noop: true
      steps:
        - log:
            message: "Uploading file: ${header.CamelFileName}"
        - toD:
            uri: azure-storage-blob://camelazure/container1
            parameters:
              blobName: "${header.CamelFileName}"
              operation: uploadBlockBlobChunked
              blockSize: 52428800
              maxConcurrency: 4
              serviceClient: "#client"
        - log:
            message: "Upload completed: ${header.CamelFileName}"
```

The `blockSize` and `maxConcurrency` options control memory usage and upload speed:

-   `blockSize`: Size of each chunk (default: 4MB, max: 4000MB). Larger blocks = fewer requests but more memory.
    
-   `maxConcurrency`: Number of parallel uploads (default: auto-detected based on CPU cores). Higher = faster but more memory.
    
-   Memory usage is approximately `blockSize × maxConcurrency`.
    

### stageBlockBlobList and commitBlobBlockList

These two operations work together for multi-block uploads. Both require Java-specific objects in the body.

_Java-only: staging blocks_

```java
from("direct:start")
    .process(exchange -> {
        final List<BlobBlock> blocks = new LinkedList<>();
        blocks.add(BlobBlock.createBlobBlock(new ByteArrayInputStream("Hello".getBytes())));
        blocks.add(BlobBlock.createBlobBlock(new ByteArrayInputStream("From".getBytes())));
        blocks.add(BlobBlock.createBlobBlock(new ByteArrayInputStream("Camel".getBytes())));
        exchange.getIn().setBody(blocks);
    })
    .to("azure-storage-blob://camelazure/container1?blobName=blob&operation=stageBlockBlobList&serviceClient=#client")
    .to("mock:result");
```

_Java-only: committing blocks_

```java
from("direct:start")
    .process(exchange -> {
        final List<Block> blockIds = new LinkedList<>();
        blockIds.add(new Block().setName("id-1"));
        blockIds.add(new Block().setName("id-2"));
        blockIds.add(new Block().setName("id-3"));
        exchange.getIn().setBody(blockIds);
    })
    .to("azure-storage-blob://camelazure/container1?blobName=blob&operation=commitBlobBlockList&serviceClient=#client")
    .to("mock:result");
```

### Page Blob Operations

The `uploadPageBlob`, `resizePageBlob`, `clearPageBlob`, and `getPageBlobRanges` operations all require a `PageRange` object set via the `CamelAzureStorageBlobPageBlobRange` header.

_Java-only: uploading to a page blob_

```java
from("direct:start")
    .process(exchange -> {
        byte[] dataBytes = new byte[512];
        new Random().nextBytes(dataBytes);
        final InputStream dataStream = new ByteArrayInputStream(dataBytes);
        final PageRange pageRange = new PageRange().setStart(0).setEnd(511);
        exchange.getIn().setHeader("CamelAzureStorageBlobPageBlobRange", pageRange);
        exchange.getIn().setBody(dataStream);
    })
    .to("azure-storage-blob://camelazure/container1?blobName=blob&operation=uploadPageBlob&serviceClient=#client")
    .to("mock:result");
```

The same pattern applies to `resizePageBlob`, `clearPageBlob`, and `getPageBlobRanges` — set the `PageRange` header and change the `operation` parameter.

### copyBlob

To copy a blob between containers (or across accounts), set the source container and account via headers:

-   Java
    
-   XML
    
-   YAML
    

```java
from("direct:copyBlob")
    .setHeader("CamelAzureStorageBlobBlobName", constant("file.txt"))
    .setHeader("CamelAzureStorageBlobSourceBlobContainerName", constant("containerblob1"))
    .setHeader("CamelAzureStorageBlobSourceBlobAccountName", constant("account"))
    .to("azure-storage-blob://account/containerblob2?operation=copyBlob&sourceBlobAccessKey=RAW(accessKey)")
    .to("mock:result");
```

```xml
<route>
  <from uri="direct:copyBlob"/>
  <setHeader name="CamelAzureStorageBlobBlobName">
    <constant>file.txt</constant>
  </setHeader>
  <setHeader name="CamelAzureStorageBlobSourceBlobContainerName">
    <constant>containerblob1</constant>
  </setHeader>
  <setHeader name="CamelAzureStorageBlobSourceBlobAccountName">
    <constant>account</constant>
  </setHeader>
  <to uri="azure-storage-blob://account/containerblob2?operation=copyBlob&amp;sourceBlobAccessKey=RAW(accessKey)"/>
  <to uri="mock:result"/>
</route>
```

```yaml
- route:
    from:
      uri: direct:copyBlob
      steps:
        - setHeader:
            name: CamelAzureStorageBlobBlobName
            constant: file.txt
        - setHeader:
            name: CamelAzureStorageBlobSourceBlobContainerName
            constant: containerblob1
        - setHeader:
            name: CamelAzureStorageBlobSourceBlobAccountName
            constant: account
        - to:
            uri: azure-storage-blob://account/containerblob2
            parameters:
              operation: copyBlob
              sourceBlobAccessKey: "RAW(accessKey)"
        - to:
            uri: mock:result
```

This copies `file.txt` from `containerblob1` to `containerblob2` within the same account.

### findBlobsByTags

Use the `CamelAzureStorageBlobTagFilter` header with a tag filter expression to find blobs across containers:

```java
from("direct:findBlobsByTags")
    .setHeader("CamelAzureStorageBlobTagFilter", constant("\"Environment\" = 'Production' AND \"Status\" = 'Active'"))
    .to("azure-storage-blob://camelazure?operation=findBlobsByTags&serviceClient=#client")
    .log("Matching blobs: ${body}")
    .to("mock:result");
```

### setBlobLegalHold

Use the `CamelAzureStorageBlobLegalHold` header with `true` to place or `false` to clear a legal hold:

```java
from("direct:setLegalHold")
    .setHeader("CamelAzureStorageBlobLegalHold", constant(true))
    .to("azure-storage-blob://camelazure/container1?blobName=hello.txt&operation=setBlobLegalHold&serviceClient=#client")
    .to("mock:result");
```

### setBlobTier

_Java-only: requires AccessTier and RehydratePriority enum objects_

```java
// move a blob to the COOL tier for less frequent access
from("direct:moveToCool")
    .setHeader("CamelAzureStorageBlobAccessTier", constant(AccessTier.COOL))
    .to("azure-storage-blob://camelazure/container1?blobName=hello.txt&operation=setBlobTier&serviceClient=#client")
    .to("mock:result");

// rehydrate a blob from ARCHIVE to HOT with high priority
from("direct:rehydrate")
    .setHeader("CamelAzureStorageBlobAccessTier", constant(AccessTier.HOT))
    .setHeader("CamelAzureStorageBlobRehydratePriority", constant(RehydratePriority.HIGH))
    .to("azure-storage-blob://camelazure/container1?blobName=archived.txt&operation=setBlobTier&serviceClient=#client")
    .to("mock:result");
```

## Reading a Specific Blob Snapshot

The `getBlob`, `downloadBlobToFile` and `downloadLink` operations can target a specific snapshot by setting the `snapshotId` URI parameter or the `CamelAzureStorageBlobSnapshotId` exchange header. When set, the read is scoped to the snapshot version of the blob instead of the live one. The header takes precedence over the URI parameter.

-   Java
    
-   XML
    
-   YAML
    

```java
from("direct:readSnapshot")
    .setHeader("CamelAzureStorageBlobSnapshotId", constant("2026-04-15T10:00:00.0000000Z"))
    .to("azure-storage-blob://camelazure/container1?blobName=hello.txt&operation=getBlob&serviceClient=#client")
    .to("mock:result");
```

```xml
<route>
  <from uri="direct:readSnapshot"/>
  <setHeader name="CamelAzureStorageBlobSnapshotId">
    <constant>2026-04-15T10:00:00.0000000Z</constant>
  </setHeader>
  <to uri="azure-storage-blob://camelazure/container1?blobName=hello.txt&amp;operation=getBlob&amp;serviceClient=#client"/>
  <to uri="mock:result"/>
</route>
```

```yaml
- route:
    from:
      uri: direct:readSnapshot
      steps:
        - setHeader:
            name: CamelAzureStorageBlobSnapshotId
            constant: "2026-04-15T10:00:00.0000000Z"
        - to:
            uri: azure-storage-blob://camelazure/container1
            parameters:
              blobName: hello.txt
              operation: getBlob
              serviceClient: "#client"
        - to:
            uri: mock:result
```

## Reading a Specific Blob Version

When blob versioning is enabled on the storage account, the `getBlob`, `downloadBlobToFile` and `downloadLink` operations can target a specific version by setting the `versionId` URI parameter or the `CamelAzureStorageBlobVersionId` exchange header. When set, the read is scoped to the version of the blob instead of the live one. The header takes precedence over the URI parameter.

-   Java
    
-   XML
    
-   YAML
    

```java
from("direct:readVersion")
    .setHeader("CamelAzureStorageBlobVersionId", constant("2026-04-15T10:00:00.0000000Z"))
    .to("azure-storage-blob://camelazure/container1?blobName=hello.txt&operation=getBlob&serviceClient=#client")
    .to("mock:result");
```

```xml
<route>
  <from uri="direct:readVersion"/>
  <setHeader name="CamelAzureStorageBlobVersionId">
    <constant>2026-04-15T10:00:00.0000000Z</constant>
  </setHeader>
  <to uri="azure-storage-blob://camelazure/container1?blobName=hello.txt&amp;operation=getBlob&amp;serviceClient=#client"/>
  <to uri="mock:result"/>
</route>
```

```yaml
- route:
    from:
      uri: direct:readVersion
      steps:
        - setHeader:
            name: CamelAzureStorageBlobVersionId
            constant: "2026-04-15T10:00:00.0000000Z"
        - to:
            uri: azure-storage-blob://camelazure/container1
            parameters:
              blobName: hello.txt
              operation: getBlob
              serviceClient: "#client"
        - to:
            uri: mock:result
```

## Blob Modification During Download (ConditionNotMet)

When downloading a blob via streaming (the `getBlob` operation or the consumer without `fileDir`), the Azure SDK reads the blob in chunks. On the first chunk it captures the blob’s ETag and uses it as an `if-match` condition on subsequent chunk requests. If the blob is modified by another process between chunks (which changes its ETag), Azure returns HTTP 412 `ConditionNotMet`.

This is by design in the Azure SDK — it ensures read consistency so that you do not receive half of one version concatenated with half of another. The error is more likely with larger blobs and slower network connections (e.g., in real Azure environments versus local emulators).

Workarounds:

-   Use the `fileDir` option so the consumer downloads the blob atomically to a file via `downloadBlobToFile` instead of streaming.
    
-   Use blob snapshots — read from an immutable snapshot ID via the `snapshotId` option or the `CamelAzureStorageBlobSnapshotId` header, which cannot be modified.
    
-   Use blob versioning — read from a specific version via the `versionId` option or the `CamelAzureStorageBlobVersionId` header.
    
-   Avoid modifying blobs while they are being consumed.
    

## SAS Token Generation Example

SAS Blob Container tokens can be generated programmatically or via Azure UI. To generate the token with Java code, the following can be done:

_Java-only: programmatic SAS token generation_

```java
BlobContainerClient blobClient = new BlobContainerClientBuilder()
        .endpoint(String.format("https://%s.blob.core.windows.net/%s", accountName, accessKey))
        .containerName(containerName)
        .credential(new StorageSharedKeyCredential(accountName, accessKey))
        .buildClient();

OffsetDateTime expiryTime = OffsetDateTime.now().plusDays(1);

BlobContainerSasPermission blobContainerSasPermission = new BlobContainerSasPermission()
        .setWritePermission(true)
        .setListPermission(true)
        .setCreatePermission(true)
        .setDeletePermission(true)
        .setAddPermission(true)
        .setReadPermission(true);

BlobServiceSasSignatureValues sasSignatureValues = new BlobServiceSasSignatureValues(expiryTime, blobContainerSasPermission);

return blobClient.generateSas(sasSignatureValues);
```

The generated SAS token can be then stored to an application.properties file so that it can be loaded by the Camel route, for example:

```properties
camel.component.azure-storage-blob.sas-token=MY_TOKEN_HERE
```

-   Java
    
-   XML
    
-   YAML
    

```java
from("direct:uploadBlob")
    .to("azure-storage-blob://account/containerblob2?operation=uploadBlockBlob&credentialType=AZURE_SAS");
```

```xml
<route>
  <from uri="direct:uploadBlob"/>
  <to uri="azure-storage-blob://account/containerblob2?operation=uploadBlockBlob&amp;credentialType=AZURE_SAS"/>
</route>
```

```yaml
- route:
    from:
      uri: direct:uploadBlob
      steps:
        - to:
            uri: azure-storage-blob://account/containerblob2
            parameters:
              operation: uploadBlockBlob
              credentialType: AZURE_SAS
```