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&operation=getBlob&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 |
|---|---|---|
| List all blob containers in the storage account. | Java-only (requires |
| Create a new blob container. | |
| Delete a blob container. | |
| List blobs in a container. | |
| List all versions of blobs in a container. Versioning must be enabled on the storage account. | Use |
| Download a blob. Returns an | See getBlob |
| Delete a blob. | |
| Download a blob directly to a file. Use the | |
| Generate a download link for a blob. The link is returned in the | |
| Upload a block blob. The body is used as the blob content. | |
| Upload a large file using chunked parallel uploads. | |
| Stage a list of blocks for later commit. | Java-only (requires |
| Commit a list of previously staged blocks. | Java-only (requires |
| Get the list of committed and uncommitted blocks for a block blob. | |
| Create an append blob. | |
| Append content to an append blob. The body is used as the content to append. | |
| Create a page blob. | |
| Upload data to a page blob at the range specified by the | Java-only (requires |
| Resize a page blob. | Java-only (requires |
| Clear a range of pages in a page blob. | Java-only (requires |
| Get the page ranges for a page blob. | Java-only (requires |
| Copy a blob from one container to another. | See copyBlob |
| Create a snapshot of a blob. The snapshot ID is returned in the | |
| Set index tags on a blob. | Java-only (requires |
| Get the index tags of a blob. | |
| Find blobs across containers by tag filter expression. | See findBlobsByTags |
| Place or clear a legal hold on a blob. | See setBlobLegalHold |
| Set an immutability policy on a blob. | Java-only (requires |
| Change the access tier of a blob (Hot, Cool, Cold, Archive). | See setBlobTier |
| 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:
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}&operation=uploadBlockBlobChunked&blockSize=52428800&maxConcurrency=4&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.
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"); 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.
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&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
// 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&operation=getBlob&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&operation=getBlob&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
fileDiroption so the consumer downloads the blob atomically to a file viadownloadBlobToFileinstead of streaming. -
Use blob snapshots — read from an immutable snapshot ID via the
snapshotIdoption or theCamelAzureStorageBlobSnapshotIdheader, which cannot be modified. -
Use blob versioning — read from a specific version via the
versionIdoption or theCamelAzureStorageBlobVersionIdheader. -
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:
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&credentialType=AZURE_SAS"/>
</route> - route:
from:
uri: direct:uploadBlob
steps:
- to:
uri: azure-storage-blob://account/containerblob2
parameters:
operation: uploadBlockBlob
credentialType: AZURE_SAS