Works with any backend npm install Zero dependencies

Direct-to-Azure Blob upload

Browser PUTs each block via Azure's PutBlock against a pre-signed blob SAS URL, then finalises with a single PutBlockList. Mirrors the S3 pattern - your server signs once, the bytes bypass it entirely. Resume is preserved via IndexedDB: stored block IDs survive reloads, only missing blocks re-upload.

Backend required. MultipleUpload is the client. Your server needs to expose 3 endpoints: /azure/create (issues a blob SAS URL), /azure/complete, /azure/abort. See CoreUpload's Azure demo for a ready-made reference implementation.
Client configuration
new MultipleUpload('#uploader', {
 uploadUrl: '/api/upload',
 strategy: 'azure',
 chunkSize: 4 * 1024 * 1024, // Azure block size
 chunkConcurrency: 4,
 persistState: true,
 persistAdapter: 'indexeddb'

 // Endpoint URLs derive from uploadUrl by default:
 // /api/azure/create, /api/azure/complete, /api/azure/abort
 // Override with azureCreateUrl, azureCompleteUrl, azureAbortUrl.
});
Required backend endpoints
  • POST /azure/create <- { fileName, fileSize, contentType } -> { uploadId, blobUrl, blockIdPrefix } - the blobUrl is a SAS URL with write permissions that the client will PUT blocks to directly
  • PUT {blobUrl}&comp=block&blockid={base64Id} per block (browser-direct, no server hop)
  • PUT {blobUrl}&comp=blocklist with XML BlockList body (browser-direct)
  • POST /azure/complete <- { uploadId, fileName, fileSize, contentType, blockCount } - server records metadata
  • POST /azure/abort on cancel / fatal error
Node reference (sketch)
import { BlobServiceClient, BlobSASPermissions } from '@azure/storage-blob';

const svc = BlobServiceClient.fromConnectionString(process.env.AZURE_STORAGE_CONNECTION);
const container = svc.getContainerClient('uploads');

app.post('/azure/create', async (req, res) => {
 const blobName = `${crypto.randomUUID()}/${req.body.fileName}`;
 const blob = container.getBlockBlobClient(blobName);
 const sasUrl = await blob.generateSasUrl({
 permissions: BlobSASPermissions.parse('cw'),
 expiresOn: new Date(Date.now() + 15 * 60 * 1000)
 });
 res.json({
 uploadId: crypto.randomUUID(),
 blobUrl: sasUrl,
 blockIdPrefix: blob.name.slice(0, 8) // any stable prefix works
 });
});
Container CORS
az storage cors add \
 --methods PUT \
 --origins https://your-site.example \
 --allowed-headers "*" \
 --exposed-headers "*" \
 --services b \
 --max-age 3600