Azure Storage Blob - Producer Operations

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

from("direct:start")
    .to("azure-storage-blob://camelazure/container1?blobName=hello.txt&operation=getBlob&serviceClient=#client")
    .to("mock:result");
<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>
- 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

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

stageBlockBlobList

Stage a list of blocks for later commit.

Java-only (requires List<BlobBlock> body)

commitBlockBlobList

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

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

setBlobLegalHold

Place or clear a legal hold on a blob.

See 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

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
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

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}");
<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>
- 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 commitBlockBlobList

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

Java-only: staging blocks
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
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=commitBlockBlobList&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
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

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");
<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>
- 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:

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:

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
// 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

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");
<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>
- 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

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");
<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>
- 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
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:

camel.component.azure-storage-blob.sas-token=MY_TOKEN_HERE
  • Java

  • XML

  • YAML

from("direct:uploadBlob")
    .to("azure-storage-blob://account/containerblob2?operation=uploadBlockBlob&credentialType=AZURE_SAS");
<route>
  <from uri="direct:uploadBlob"/>
  <to uri="azure-storage-blob://account/containerblob2?operation=uploadBlockBlob&amp;credentialType=AZURE_SAS"/>
</route>
- route:
    from:
      uri: direct:uploadBlob
      steps:
        - to:
            uri: azure-storage-blob://account/containerblob2
            parameters:
              operation: uploadBlockBlob
              credentialType: AZURE_SAS