Download Azure Blob Storage Files as a ZIP
List a container prefix, generate SAS URLs on your server, and package the results into one ZIP with @eazip/core in the browser.
Azure Blob Storage has no built-in "download container as ZIP" action, so you
list the blobs under a prefix, generate a short-lived SAS URL for each one,
and hand the resulting { url, filename } list to @eazip/core. Storage
account keys stay on the server, and the browser does the archiving.
Loading live demo…
Prepare SAS URLs on the server
Your backend endpoint should:
- authenticate the application user;
- list the blobs under the requested container and prefix;
- authorize the request against those specific blobs; and
- return short-lived SAS URLs, never the storage account key.
import {
BlobServiceClient,
StorageSharedKeyCredential,
BlobSASPermissions,
} from '@azure/storage-blob';
const credential = new StorageSharedKeyCredential(
process.env.AZURE_STORAGE_ACCOUNT!,
process.env.AZURE_STORAGE_KEY!,
);
const blobServiceClient = new BlobServiceClient(
`https://${process.env.AZURE_STORAGE_ACCOUNT}.blob.core.windows.net`,
credential,
);
type SignedSource = {
url: string;
filename: string;
};
export async function listContainerAsSignedUrls(
containerName: string,
prefix: string,
): Promise<SignedSource[]> {
const containerClient = blobServiceClient.getContainerClient(containerName);
const expiresOn = new Date(Date.now() + 15 * 60 * 1000);
const sources: SignedSource[] = [];
for await (const blob of containerClient.listBlobsFlat({ prefix })) {
const blobClient = containerClient.getBlobClient(blob.name);
const url = await blobClient.generateSasUrl({
permissions: BlobSASPermissions.parse('r'),
expiresOn,
});
sources.push({ url, filename: blob.name.slice(prefix.length) });
}
return sources;
}listBlobsFlat({ prefix }) returns an async iterable, so there is no
continuation-token loop to write yourself. Keep expiresOn short, but long
enough for every file to finish downloading; a URL that expires mid-fetch
fails only that file, and the archive is still usable if you leave
failOnUrlError at its default.
generateSasUrl() needs a client built with an account key, as above. If your
service uses Azure AD or a managed identity instead, call
blobServiceClient.getUserDelegationKey() and pass the result to
generateBlobSASQueryParameters() in place of the account key credential; the
returned query string appends to the blob's URL the same way.
Configure Blob service CORS
The browser fetches each blob directly from Azure, so the storage account must allow your application's origin. Add a Blob service CORS rule with the Azure CLI:
az storage cors add \
--account-name youraccount \
--services b \
--methods GET HEAD OPTIONS \
--origins "https://app.example.com" \
--allowed-headers "*" \
--exposed-headers "*" \
--max-age 3600Or set the same rule in the portal under the storage account's Settings > Resource sharing (CORS), on the Blob service tab. Without this, the browser blocks the fetch and Eazip reports the file as failed even though the SAS URL itself is valid.
Create the ZIP in the browser
import { createZip } from '@eazip/core';
const response = await fetch('/api/exports/container-prefix', {
credentials: 'include',
});
if (!response.ok) {
throw new Error('Could not prepare export files');
}
const files: { url: string; filename: string }[] = await response.json();files also accepts plain URL strings, or a mix of URLs and browser File or
Blob objects if part of the export comes from a local picker; see
Input types for every accepted shape.
const result = await createZip({
files,
zipName: 'blob-export.zip',
});
result.download();createZip() resolves once every reachable blob has been fetched and
packaged, then result.download() starts the browser download. Use
startZip() instead when you need live progress or a cancel button; see
Create a ZIP from remote URLs for
that shape and for how partial results behave when some blobs fail.
When to use Eazip Cloud
Local jobs like the one above run entirely in the visitor's tab. Switch to Eazip Cloud when:
- the prefix totals multiple gigabytes or thousands of blobs;
- the storage account cannot allow browser CORS, for example a locked-down compliance account;
- the export should survive a page reload; or
- you would rather not hold archive bytes in tab memory at all.
Cloud accepts the same SAS URL list; only the strategy changes:
const result = await createZip({
strategy: 'cloud',
publicKey: 'pk_ez_...',
files,
zipName: 'blob-export.zip',
});
result.downloadAll();Add the storage account's hostname to the Public App's allowed source hosts,
and make the SAS expiresOn long enough for Eazip Cloud to start and finish
reading each blob. See When to use Eazip Cloud for the full
comparison with Local.
Limits and alternatives
Every approach here still pays Azure's standard outbound data transfer pricing: blobs served to a fetch in the browser, or to Eazip Cloud, count as egress from the storage account, typically billed after the first 100 GB/month across your Azure subscription (rates vary by region and tier). Cloud's stream mode removes the archive from your visitor's browser, but it does not remove that underlying egress cost — someone still fetches every byte from Azure once.
Eazip is not the right tool for every Blob Storage download:
- A single blob doesn't need a ZIP at all; return one SAS URL and let the browser download it directly.
- A bulk copy between storage accounts or to a local disk is better served by azcopy, Microsoft's own CLI, which is built for that job and does not go through a browser tab at all.
- A server-side archive, built with an Azure Function that streams blobs into a ZIP and writes the result back to storage, avoids sending bytes through the visitor's browser, but you take on the Function's execution time limit and temporary storage limit yourself. Eazip Cloud exists largely so you don't have to build and operate that path.
FAQ
How do I download an entire Azure Blob Storage container as a ZIP?
List the blobs under the container (or a prefix) from your server, generate a
SAS URL for each one, and pass the { url, filename } list to
createZip() from @eazip/core. Azure has no native "download container as
ZIP" operation, so the listing and archiving both happen outside the storage
service itself.
Can I download multiple files from Azure Blob Storage as one ZIP?
Yes. createZip() accepts an array of SAS URLs and produces one ZIP in the
browser, or in Eazip Cloud for larger sets. See
Create a ZIP from remote URLs for
the general shape, and the example above for the Azure-specific listing and
signing step.
Why does my Azure SAS URL ZIP download fail with a CORS error?
The browser is fetching each SAS URL directly from Azure, and the storage
account has no CORS rule allowing your origin. Add the CORS rule shown above,
and confirm AllowedMethods includes GET for the app's exact origin.
Do I need an Azure AD identity to generate the SAS URLs?
No. An account-key credential is enough, as shown above. Use a user
delegation key with getUserDelegationKey() instead if your service already
authenticates with Azure AD or a managed identity and you would rather avoid
distributing the account key.
How large a container can Eazip zip?
A Local job is limited by the browser tab's memory and the tab staying open.
For anything multi-gigabyte, thousands of blobs, or that needs to survive a
reload, use strategy: 'cloud' as shown above.
Download Google Cloud Storage Files as a ZIP
List a bucket prefix, sign V4 read URLs, and package the objects into one ZIP with @eazip/core in the browser.
Download DigitalOcean Spaces Files as a ZIP
Presign DigitalOcean Spaces objects on the server, then package them into one browser-downloaded ZIP with Eazip.