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 }- theblobUrlis a SAS URL with write permissions that the client will PUT blocks to directlyPUT {blobUrl}&comp=block&blockid={base64Id}per block (browser-direct, no server hop)PUT {blobUrl}&comp=blocklistwith XML BlockList body (browser-direct)POST /azure/complete<-{ uploadId, fileName, fileSize, contentType, blockCount }- server records metadataPOST /azure/aborton 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