GitHub Copilot ChatGPT Claude Codex CLI Cursor opencode Skill Text

azure-storage-blob-ts

Azure Blob Storage JavaScript/TypeScript SDK (@azure/storage-blob) for blob operations. Use for uploading, downloading, listing, and managing blobs and containers. Supports block blobs, append blobs, page blobs, SAS tokens, and streaming. Triggers: "blob storage", "@azure/storage

Ciza · 0 points · 27 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download microsoft-skills-.github_plugins_azure-sdk-typescript_skills_azure-storage-blob-ts-e58528d.zip · 10 KB
Part of microsoft/skills — 195 skills

Install

skills CLI npx skills add https://github.com/microsoft/skills/tree/main/.github/plugins/azure-sdk-typescript/skills/azure-storage-blob-ts
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install microsoft-skills@llmmart
Git git clone https://github.com/microsoft/skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole microsoft/skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

@azure/storage-blob (TypeScript/JavaScript)

SDK for Azure Blob Storage operations — upload, download, list, and manage blobs and containers.

Installation

npm install @azure/storage-blob @azure/identity

Current Version: 12.x
Node.js: >= 18.0.0

Environment Variables

AZURE_STORAGE_ACCOUNT_NAME=<account-name>
AZURE_STORAGE_ACCOUNT_KEY=<account-key>
# OR connection string
AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=...
AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production

Authentication

Microsoft Entra Token Credential (Recommended)

import { BlobServiceClient } from "@azure/storage-blob";
import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";

// Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]});
// Or use a specific credential directly in production:
// See https://learn.microsoft.com/javascript/api/overview/azure/identity-readme?view=azure-node-latest#credential-classes
// const credential = new ManagedIdentityCredential();

const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;
const client = new BlobServiceClient(
  `https://${accountName}.blob.core.windows.net`,
  credential
);

Connection String

import { BlobServiceClient } from "@azure/storage-blob";

const client = BlobServiceClient.fromConnectionString(
  process.env.AZURE_STORAGE_CONNECTION_STRING!
);

StorageSharedKeyCredential (Node.js only)

import { BlobServiceClient, StorageSharedKeyCredential } from "@azure/storage-blob";

const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;
const accountKey = process.env.AZURE_STORAGE_ACCOUNT_KEY!;

const sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);
const client = new BlobServiceClient(
  `https://${accountName}.blob.core.windows.net`,
  sharedKeyCredential
);

SAS Token

import { BlobServiceClient } from "@azure/storage-blob";

const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;
const sasToken = process.env.AZURE_STORAGE_SAS_TOKEN!; // starts with "?"

const client = new BlobServiceClient(
  `https://${accountName}.blob.core.windows.net${sasToken}`
);

Client Hierarchy

BlobServiceClient (account level)
└── ContainerClient (container level)
    └── BlobClient (blob level)
        ├── BlockBlobClient (block blobs - most common)
        ├── AppendBlobClient (append-only blobs)
        └── PageBlobClient (page blobs - VHDs)

Container Operations

Create Container

const containerClient = client.getContainerClient("my-container");
await containerClient.create();

// Or create if not exists
await containerClient.createIfNotExists();

List Containers

for await (const container of client.listContainers()) {
  console.log(container.name);
}

// With prefix filter
for await (const container of client.listContainers({ prefix: "logs-" })) {
  console.log(container.name);
}

Delete Container

await containerClient.delete();
// Or delete if exists
await containerClient.deleteIfExists();

Blob Operations

Upload Blob (Simple)

const containerClient = client.getContainerClient("my-container");
const blockBlobClient = containerClient.getBlockBlobClient("my-file.txt");

// Upload string
await blockBlobClient.upload("Hello, World!", 13);

// Upload Buffer
const buffer = Buffer.from("Hello, World!");
await blockBlobClient.upload(buffer, buffer.length);

Upload from File (Node.js only)

const blockBlobClient = containerClient.getBlockBlobClient("uploaded-file.txt");
await blockBlobClient.uploadFile("/path/to/local/file.txt");

Upload from Stream (Node.js only)

import * as fs from "fs";

const blockBlobClient = containerClient.getBlockBlobClient("streamed-file.txt");
const readStream = fs.createReadStream("/path/to/local/file.txt");

await blockBlobClient.uploadStream(readStream, 4 * 1024 * 1024, 5, {
  // bufferSize: 4MB, maxConcurrency: 5
  onProgress: (progress) => console.log(`Uploaded ${progress.loadedBytes} bytes`),
});

Upload from Browser

const blockBlobClient = containerClient.getBlockBlobClient("browser-upload.txt");

// From File input
const fileInput = document.getElementById("fileInput") as HTMLInputElement;
const file = fileInput.files![0];
await blockBlobClient.uploadData(file);

// From Blob/ArrayBuffer
const arrayBuffer = new ArrayBuffer(1024);
await blockBlobClient.uploadData(arrayBuffer);

Download Blob

const blobClient = containerClient.getBlobClient("my-file.txt");
const downloadResponse = await blobClient.download();

// Read as string (browser & Node.js)
const downloaded = await streamToText(downloadResponse.readableStreamBody!);

async function streamToText(readable: NodeJS.ReadableStream): Promise<string> {
  const chunks: Buffer[] = [];
  for await (const chunk of readable) {
    chunks.push(Buffer.from(chunk));
  }
  return Buffer.concat(chunks).toString("utf-8");
}

Download to File (Node.js only)

const blockBlobClient = containerClient.getBlockBlobClient("my-file.txt");
await blockBlobClient.downloadToFile("/path/to/local/destination.txt");

Download to Buffer (Node.js only)

const blockBlobClient = containerClient.getBlockBlobClient("my-file.txt");
const buffer = await blockBlobClient.downloadToBuffer();
console.log(buffer.toString());

List Blobs

// List all blobs
for await (const blob of containerClient.listBlobsFlat()) {
  console.log(blob.name, blob.properties.contentLength);
}

// List with prefix
for await (const blob of containerClient.listBlobsFlat({ prefix: "logs/" })) {
  console.log(blob.name);
}

// List by hierarchy (virtual directories)
for await (const item of containerClient.listBlobsByHierarchy("/")) {
  if (item.kind === "prefix") {
    console.log(`Directory: ${item.name}`);
  } else {
    console.log(`Blob: ${item.name}`);
  }
}

Delete Blob

const blobClient = containerClient.getBlobClient("my-file.txt");
await blobClient.delete();

// Delete if exists
await blobClient.deleteIfExists();

// Delete with snapshots
await blobClient.delete({ deleteSnapshots: "include" });

Copy Blob

const sourceBlobClient = containerClient.getBlobClient("source.txt");
const destBlobClient = containerClient.getBlobClient("destination.txt");

// Start copy operation
const copyPoller = await destBlobClient.beginCopyFromURL(sourceBlobClient.url);
await copyPoller.pollUntilDone();

Blob Properties & Metadata

Get Properties

const blobClient = containerClient.getBlobClient("my-file.txt");
const properties = await blobClient.getProperties();

console.log("Content-Type:", properties.contentType);
console.log("Content-Length:", properties.contentLength);
console.log("Last Modified:", properties.lastModified);
console.log("ETag:", properties.etag);

Set Metadata

await blobClient.setMetadata({
  author: "John Doe",
  category: "documents",
});

Set HTTP Headers

await blobClient.setHTTPHeaders({
  blobContentType: "text/plain",
  blobCacheControl: "max-age=3600",
  blobContentDisposition: "attachment; filename=download.txt",
});

SAS Token Generation (Node.js only)

Generate Blob SAS

import {
  BlobSASPermissions,
  generateBlobSASQueryParameters,
  StorageSharedKeyCredential,
} from "@azure/storage-blob";

const sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);

const sasToken = generateBlobSASQueryParameters(
  {
    containerName: "my-container",
    blobName: "my-file.txt",
    permissions: BlobSASPermissions.parse("r"), // read only
    startsOn: new Date(),
    expiresOn: new Date(Date.now() + 3600 * 1000), // 1 hour
  },
  sharedKeyCredential
).toString();

const sasUrl = `https://${accountName}.blob.core.windows.net/my-container/my-file.txt?${sasToken}`;

Generate Container SAS

import { ContainerSASPermissions, generateBlobSASQueryParameters } from "@azure/storage-blob";

const sasToken = generateBlobSASQueryParameters(
  {
    containerName: "my-container",
    permissions: ContainerSASPermissions.parse("racwdl"), // read, add, create, write, delete, list
    expiresOn: new Date(Date.now() + 24 * 3600 * 1000), // 24 hours
  },
  sharedKeyCredential
).toString();

Generate Account SAS

import {
  AccountSASPermissions,
  AccountSASResourceTypes,
  AccountSASServices,
  generateAccountSASQueryParameters,
} from "@azure/storage-blob";

const sasToken = generateAccountSASQueryParameters(
  {
    services: AccountSASServices.parse("b").toString(), // blob
    resourceTypes: AccountSASResourceTypes.parse("sco").toString(), // service, container, object
    permissions: AccountSASPermissions.parse("rwdlacupi"), // all permissions
    expiresOn: new Date(Date.now() + 24 * 3600 * 1000),
  },
  sharedKeyCredential
).toString();

Blob Types

Block Blob (Default)

Most common type for text and binary files.

const blockBlobClient = containerClient.getBlockBlobClient("document.pdf");
await blockBlobClient.uploadFile("/path/to/document.pdf");

Append Blob

Optimized for append operations (logs, audit trails).

const appendBlobClient = containerClient.getAppendBlobClient("app.log");

// Create the append blob
await appendBlobClient.create();

// Append data
await appendBlobClient.appendBlock("Log entry 1\n", 12);
await appendBlobClient.appendBlock("Log entry 2\n", 12);

Page Blob

Fixed-size blobs for random read/write (VHDs).

const pageBlobClient = containerClient.getPageBlobClient("disk.vhd");

// Create 512-byte aligned page blob
await pageBlobClient.create(1024 * 1024); // 1MB

// Write pages (must be 512-byte aligned)
const buffer = Buffer.alloc(512);
await pageBlobClient.uploadPages(buffer, 0, 512);

Error Handling

import { RestError } from "@azure/storage-blob";

try {
  await containerClient.create();
} catch (error) {
  if (error instanceof RestError) {
    switch (error.statusCode) {
      case 404:
        console.log("Container not found");
        break;
      case 409:
        console.log("Container already exists");
        break;
      case 403:
        console.log("Access denied");
        break;
      default:
        console.error(`Storage error ${error.statusCode}: ${error.message}`);
    }
  }
  throw error;
}

TypeScript Types Reference

import {
  // Clients
  BlobServiceClient,
  ContainerClient,
  BlobClient,
  BlockBlobClient,
  AppendBlobClient,
  PageBlobClient,

  // Authentication
  StorageSharedKeyCredential,
  AnonymousCredential,

  // SAS
  BlobSASPermissions,
  ContainerSASPermissions,
  AccountSASPermissions,
  AccountSASServices,
  AccountSASResourceTypes,
  generateBlobSASQueryParameters,
  generateAccountSASQueryParameters,

  // Options & Responses
  BlobDownloadResponseParsed,
  BlobUploadCommonResponse,
  ContainerCreateResponse,
  BlobItem,
  ContainerItem,

  // Errors
  RestError,
} from "@azure/storage-blob";

Best Practices

  1. Use DefaultAzureCredential for local development; use ManagedIdentityCredential or WorkloadIdentityCredential for production
  2. Use streaming for large files — uploadStream/downloadToFile for files > 256MB
  3. Set appropriate content types — Use setHTTPHeaders for correct MIME types
  4. Use SAS tokens for client access — Generate short-lived tokens for browser uploads
  5. Handle errors gracefully — Check RestError.statusCode for specific handling
  6. Use *IfNotExists methods — For idempotent container/blob creation
  7. Close clients — Not required but good practice in long-running apps

Platform Differences

Feature Node.js Browser
StorageSharedKeyCredential ✅ ❌
uploadFile() ✅ ❌
uploadStream() ✅ ❌
downloadToFile() ✅ ❌
downloadToBuffer() ✅ ❌
uploadData() ✅ ✅
SAS generation ✅ ❌
DefaultAzureCredential ✅ ❌
Anonymous/SAS access ✅ ✅
Files (skills)
  • references
    • sas-tokens.md 12.7 KB
      # @azure/storage-blob - SAS Token Patterns
      
      Reference documentation for generating Shared Access Signatures (SAS) in the Azure Blob Storage TypeScript SDK.
      
      **Source**: [Azure SDK for JS - storage-blob](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/storage/storage-blob)
      
      ---
      
      ## Installation
      
      ```bash
      npm install @azure/storage-blob @azure/identity
      ```
      
      ---
      
      ## SAS Types Overview
      
      | SAS Type | Scope | Use Case |
      |----------|-------|----------|
      | **Service SAS** | Single blob or container | Grant access to specific resources |
      | **Account SAS** | Entire storage account | Grant broad access to multiple services |
      | **User Delegation SAS** | Single blob or container | Most secure, uses Entra ID credentials |
      
      ---
      
      ## User Delegation SAS (Recommended)
      
      Most secure option—uses Entra ID credentials instead of account keys.
      
      ### Generate User Delegation Key
      
      ```typescript
      import {
        BlobServiceClient,
        generateBlobSASQueryParameters,
        BlobSASPermissions,
        SASProtocol,
      } from "@azure/storage-blob";
      import { DefaultAzureCredential } from "@azure/identity";
      
      const credential = new DefaultAzureCredential();
      const blobServiceClient = new BlobServiceClient(
        `https://${accountName}.blob.core.windows.net`,
        credential
      );
      
      // Get user delegation key (valid for up to 7 days)
      const startsOn = new Date();
      const expiresOn = new Date(startsOn.valueOf() + 3600 * 1000); // 1 hour
      
      const userDelegationKey = await blobServiceClient.getUserDelegationKey(
        startsOn,
        expiresOn
      );
      ```
      
      ### Generate User Delegation SAS for Blob
      
      ```typescript
      const containerName = "my-container";
      const blobName = "my-blob.txt";
      
      const sasToken = generateBlobSASQueryParameters(
        {
          containerName,
          blobName,
          permissions: BlobSASPermissions.parse("r"), // read only
          startsOn,
          expiresOn,
          protocol: SASProtocol.Https,
        },
        userDelegationKey,
        accountName
      ).toString();
      
      const sasUrl = `https://${accountName}.blob.core.windows.net/${containerName}/${blobName}?${sasToken}`;
      console.log("SAS URL:", sasUrl);
      ```
      
      ---
      
      ## Service SAS (Account Key)
      
      Uses storage account key. Less secure but simpler for some scenarios.
      
      ### Generate Blob SAS
      
      ```typescript
      import {
        BlobServiceClient,
        generateBlobSASQueryParameters,
        BlobSASPermissions,
        StorageSharedKeyCredential,
      } from "@azure/storage-blob";
      
      const accountName = process.env["STORAGE_ACCOUNT_NAME"]!;
      const accountKey = process.env["STORAGE_ACCOUNT_KEY"]!;
      
      const sharedKeyCredential = new StorageSharedKeyCredential(
        accountName,
        accountKey
      );
      
      const sasToken = generateBlobSASQueryParameters(
        {
          containerName: "my-container",
          blobName: "my-blob.txt",
          permissions: BlobSASPermissions.parse("racwd"), // read, add, create, write, delete
          startsOn: new Date(),
          expiresOn: new Date(Date.now() + 3600 * 1000), // 1 hour
        },
        sharedKeyCredential
      ).toString();
      
      const sasUrl = `https://${accountName}.blob.core.windows.net/my-container/my-blob.txt?${sasToken}`;
      ```
      
      ### Generate Container SAS
      
      ```typescript
      import {
        ContainerSASPermissions,
        generateBlobSASQueryParameters,
      } from "@azure/storage-blob";
      
      const containerSasToken = generateBlobSASQueryParameters(
        {
          containerName: "my-container",
          permissions: ContainerSASPermissions.parse("rl"), // read, list
          startsOn: new Date(),
          expiresOn: new Date(Date.now() + 86400 * 1000), // 24 hours
        },
        sharedKeyCredential
      ).toString();
      
      const containerSasUrl = `https://${accountName}.blob.core.windows.net/my-container?${containerSasToken}`;
      ```
      
      ---
      
      ## Account SAS
      
      Grants access to multiple services (blob, queue, table, file).
      
      ```typescript
      import {
        generateAccountSASQueryParameters,
        AccountSASPermissions,
        AccountSASServices,
        AccountSASResourceTypes,
        StorageSharedKeyCredential,
      } from "@azure/storage-blob";
      
      const accountSasToken = generateAccountSASQueryParameters(
        {
          services: AccountSASServices.parse("btqf").toString(), // blob, table, queue, file
          resourceTypes: AccountSASResourceTypes.parse("sco").toString(), // service, container, object
          permissions: AccountSASPermissions.parse("rwdlacupi"), // all permissions
          startsOn: new Date(),
          expiresOn: new Date(Date.now() + 3600 * 1000),
          protocol: SASProtocol.Https,
        },
        sharedKeyCredential
      ).toString();
      
      const accountSasUrl = `https://${accountName}.blob.core.windows.net?${accountSasToken}`;
      ```
      
      ---
      
      ## SAS Permissions
      
      ### Blob Permissions (`BlobSASPermissions`)
      
      | Permission | Code | Description |
      |------------|------|-------------|
      | Read | `r` | Read blob content and metadata |
      | Add | `a` | Add blocks to append blob |
      | Create | `c` | Create new blob |
      | Write | `w` | Write to blob |
      | Delete | `d` | Delete blob |
      | Delete Version | `x` | Delete blob version |
      | Tag | `t` | Read/write blob tags |
      | Move | `m` | Move blob |
      | Execute | `e` | Execute (for DataLake) |
      | Set Immutability | `i` | Set immutability policy |
      | Permanent Delete | `y` | Permanently delete (soft-deleted) |
      
      ```typescript
      // Parse from string
      const permissions = BlobSASPermissions.parse("rwd");
      
      // Build programmatically
      const permissions = new BlobSASPermissions();
      permissions.read = true;
      permissions.write = true;
      permissions.delete = true;
      ```
      
      ### Container Permissions (`ContainerSASPermissions`)
      
      Same as blob permissions, plus:
      
      | Permission | Code | Description |
      |------------|------|-------------|
      | List | `l` | List blobs in container |
      
      ```typescript
      const permissions = ContainerSASPermissions.parse("rl"); // read and list
      ```
      
      ### Account Permissions (`AccountSASPermissions`)
      
      | Permission | Code | Description |
      |------------|------|-------------|
      | Read | `r` | Read |
      | Write | `w` | Write |
      | Delete | `d` | Delete |
      | Delete Version | `x` | Delete version |
      | List | `l` | List |
      | Add | `a` | Add |
      | Create | `c` | Create |
      | Update | `u` | Update |
      | Process | `p` | Process messages |
      | Tag | `t` | Tags |
      | Filter | `f` | Filter by tags |
      | Set Immutability | `i` | Immutability policy |
      
      ---
      
      ## SAS Options
      
      ### Content Headers
      
      Override response headers:
      
      ```typescript
      const sasToken = generateBlobSASQueryParameters(
        {
          containerName: "my-container",
          blobName: "document.pdf",
          permissions: BlobSASPermissions.parse("r"),
          expiresOn: new Date(Date.now() + 3600 * 1000),
          
          // Override response headers
          contentDisposition: "attachment; filename=download.pdf",
          contentType: "application/pdf",
          cacheControl: "max-age=3600",
          contentEncoding: "gzip",
          contentLanguage: "en-US",
        },
        sharedKeyCredential
      ).toString();
      ```
      
      ### IP Restrictions
      
      Restrict SAS to specific IP addresses:
      
      ```typescript
      const sasToken = generateBlobSASQueryParameters(
        {
          containerName: "my-container",
          blobName: "my-blob.txt",
          permissions: BlobSASPermissions.parse("r"),
          expiresOn: new Date(Date.now() + 3600 * 1000),
          
          // Single IP
          ipRange: { start: "168.1.5.60" },
          
          // IP range
          // ipRange: { start: "168.1.5.60", end: "168.1.5.70" },
        },
        sharedKeyCredential
      ).toString();
      ```
      
      ### Protocol Restriction
      
      Require HTTPS:
      
      ```typescript
      import { SASProtocol } from "@azure/storage-blob";
      
      const sasToken = generateBlobSASQueryParameters(
        {
          containerName: "my-container",
          blobName: "my-blob.txt",
          permissions: BlobSASPermissions.parse("r"),
          expiresOn: new Date(Date.now() + 3600 * 1000),
          protocol: SASProtocol.Https, // HTTPS only
          // protocol: SASProtocol.HttpsAndHttp, // Allow both
        },
        sharedKeyCredential
      ).toString();
      ```
      
      ### Blob Versioning
      
      Access specific blob version or snapshot:
      
      ```typescript
      const sasToken = generateBlobSASQueryParameters(
        {
          containerName: "my-container",
          blobName: "my-blob.txt",
          permissions: BlobSASPermissions.parse("r"),
          expiresOn: new Date(Date.now() + 3600 * 1000),
          
          // For snapshots
          snapshotTime: "2023-01-15T10:30:00.0000000Z",
          
          // For versions
          // versionId: "2023-01-15T10:30:00.0000000Z",
        },
        sharedKeyCredential
      ).toString();
      ```
      
      ---
      
      ## Using SAS URLs
      
      ### Download with SAS
      
      ```typescript
      // Client-side (browser or Node.js)
      const response = await fetch(sasUrl);
      const blob = await response.blob();
      ```
      
      ### Upload with SAS (Write Permission)
      
      ```typescript
      const sasUrlWithWrite = `https://${accountName}.blob.core.windows.net/container/blob.txt?${writeSasToken}`;
      
      await fetch(sasUrlWithWrite, {
        method: "PUT",
        headers: {
          "x-ms-blob-type": "BlockBlob",
          "Content-Type": "text/plain",
        },
        body: "Hello, World!",
      });
      ```
      
      ### Create Anonymous Client with SAS
      
      ```typescript
      import { BlobClient } from "@azure/storage-blob";
      
      // No credential needed - SAS in URL
      const blobClient = new BlobClient(sasUrl);
      const downloadResponse = await blobClient.download(0);
      ```
      
      ---
      
      ## Complete Example
      
      ```typescript
      import {
        BlobServiceClient,
        generateBlobSASQueryParameters,
        BlobSASPermissions,
        ContainerSASPermissions,
        SASProtocol,
        StorageSharedKeyCredential,
      } from "@azure/storage-blob";
      import { DefaultAzureCredential } from "@azure/identity";
      
      async function generateSasTokens() {
        const accountName = process.env["STORAGE_ACCOUNT_NAME"]!;
        const accountKey = process.env["STORAGE_ACCOUNT_KEY"]!;
        
        // Method 1: User Delegation SAS (recommended)
        const credential = new DefaultAzureCredential();
        const blobServiceClient = new BlobServiceClient(
          `https://${accountName}.blob.core.windows.net`,
          credential
        );
        
        const startsOn = new Date();
        const expiresOn = new Date(startsOn.valueOf() + 3600 * 1000);
        
        const userDelegationKey = await blobServiceClient.getUserDelegationKey(
          startsOn,
          expiresOn
        );
        
        const userDelegationSas = generateBlobSASQueryParameters(
          {
            containerName: "uploads",
            blobName: "user-file.txt",
            permissions: BlobSASPermissions.parse("r"),
            startsOn,
            expiresOn,
            protocol: SASProtocol.Https,
          },
          userDelegationKey,
          accountName
        ).toString();
        
        console.log("User Delegation SAS URL:");
        console.log(`https://${accountName}.blob.core.windows.net/uploads/user-file.txt?${userDelegationSas}`);
        
        // Method 2: Service SAS with account key
        const sharedKeyCredential = new StorageSharedKeyCredential(
          accountName,
          accountKey
        );
        
        // Read-only SAS for download
        const readSas = generateBlobSASQueryParameters(
          {
            containerName: "public-files",
            blobName: "document.pdf",
            permissions: BlobSASPermissions.parse("r"),
            expiresOn: new Date(Date.now() + 86400 * 1000), // 24 hours
            contentDisposition: "attachment; filename=download.pdf",
            protocol: SASProtocol.Https,
          },
          sharedKeyCredential
        ).toString();
        
        console.log("\nRead SAS URL:");
        console.log(`https://${accountName}.blob.core.windows.net/public-files/document.pdf?${readSas}`);
        
        // Write SAS for upload
        const writeSas = generateBlobSASQueryParameters(
          {
            containerName: "uploads",
            blobName: "new-upload.txt",
            permissions: BlobSASPermissions.parse("cw"), // create, write
            expiresOn: new Date(Date.now() + 3600 * 1000), // 1 hour
            protocol: SASProtocol.Https,
          },
          sharedKeyCredential
        ).toString();
        
        console.log("\nWrite SAS URL:");
        console.log(`https://${accountName}.blob.core.windows.net/uploads/new-upload.txt?${writeSas}`);
        
        // Container list SAS
        const listSas = generateBlobSASQueryParameters(
          {
            containerName: "public-files",
            permissions: ContainerSASPermissions.parse("rl"), // read, list
            expiresOn: new Date(Date.now() + 3600 * 1000),
            protocol: SASProtocol.Https,
          },
          sharedKeyCredential
        ).toString();
        
        console.log("\nContainer List SAS URL:");
        console.log(`https://${accountName}.blob.core.windows.net/public-files?${listSas}`);
      }
      
      generateSasTokens().catch(console.error);
      ```
      
      ---
      
      ## Best Practices
      
      1. **Prefer User Delegation SAS** - More secure, tied to Entra ID, auditable
      2. **Use shortest possible expiry** - Minimize exposure window
      3. **Use HTTPS only** - Set `protocol: SASProtocol.Https`
      4. **Apply least privilege** - Grant only necessary permissions
      5. **Use IP restrictions** - When client IPs are known
      6. **Rotate keys regularly** - If using account key SAS
      7. **Store SAS securely** - Never expose in client-side code permanently
      8. **Set start time** - Prevent "not yet valid" errors due to clock skew
      9. **Use stored access policies** - For container-level SAS management
      
      ---
      
      ## Security Considerations
      
      | Risk | Mitigation |
      |------|------------|
      | SAS token leaked | Short expiry, IP restrictions, HTTPS only |
      | Over-privileged access | Least privilege permissions |
      | Account key compromise | Use User Delegation SAS instead |
      | Clock skew issues | Set `startsOn` slightly in the past |
      | Token reuse | Use unique blob names or short expiry |
      
      ---
      
      ## See Also
      
      - [Streaming Patterns](./streaming.md) - Upload/download with SAS
      - [Official Documentation](https://learn.microsoft.com/azure/storage/common/storage-sas-overview)
      
    • streaming.md 10.2 KB
      # @azure/storage-blob - Streaming Patterns
      
      Reference documentation for upload/download streaming in the Azure Blob Storage TypeScript SDK.
      
      **Source**: [Azure SDK for JS - storage-blob](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/storage/storage-blob)
      
      ---
      
      ## Installation
      
      ```bash
      npm install @azure/storage-blob @azure/identity
      ```
      
      ---
      
      ## Client Setup
      
      ```typescript
      import {
        BlobServiceClient,
        ContainerClient,
        BlockBlobClient,
      } from "@azure/storage-blob";
      import { DefaultAzureCredential } from "@azure/identity";
      
      const credential = new DefaultAzureCredential();
      const accountUrl = `https://${process.env["STORAGE_ACCOUNT_NAME"]}.blob.core.windows.net`;
      
      const blobServiceClient = new BlobServiceClient(accountUrl, credential);
      const containerClient = blobServiceClient.getContainerClient("my-container");
      const blobClient = containerClient.getBlockBlobClient("my-blob.txt");
      ```
      
      ---
      
      ## Download Streaming
      
      ### Download to Buffer
      
      ```typescript
      const downloadResponse = await blobClient.download(0);
      const downloaded = await streamToBuffer(downloadResponse.readableStreamBody!);
      
      async function streamToBuffer(readableStream: NodeJS.ReadableStream): Promise<Buffer> {
        return new Promise((resolve, reject) => {
          const chunks: Buffer[] = [];
          readableStream.on("data", (data) => chunks.push(Buffer.from(data)));
          readableStream.on("end", () => resolve(Buffer.concat(chunks)));
          readableStream.on("error", reject);
        });
      }
      ```
      
      ### Download to File (Node.js)
      
      ```typescript
      import fs from "node:fs";
      import { pipeline } from "node:stream/promises";
      
      const downloadResponse = await blobClient.download(0);
      await pipeline(
        downloadResponse.readableStreamBody!,
        fs.createWriteStream("./local-file.txt")
      );
      ```
      
      ### Download with Range
      
      ```typescript
      // Download bytes 100-199 (100 bytes total)
      const downloadResponse = await blobClient.download(100, 100);
      ```
      
      ### Download with Progress
      
      ```typescript
      const downloadResponse = await blobClient.download(0, undefined, {
        onProgress: (progress) => {
          console.log(`Downloaded ${progress.loadedBytes} bytes`);
        },
      });
      ```
      
      ---
      
      ## Upload Streaming
      
      ### Upload from Buffer
      
      ```typescript
      const content = Buffer.from("Hello, World!");
      await blobClient.upload(content, content.length);
      ```
      
      ### Upload from Stream (Node.js)
      
      ```typescript
      import fs from "node:fs";
      
      const fileStream = fs.createReadStream("./large-file.zip");
      const fileSize = fs.statSync("./large-file.zip").size;
      
      await blobClient.uploadStream(fileStream, fileSize, 4, {
        onProgress: (progress) => {
          const percent = ((progress.loadedBytes / fileSize) * 100).toFixed(2);
          console.log(`Upload progress: ${percent}%`);
        },
      });
      ```
      
      ### Upload with Options
      
      ```typescript
      await blobClient.uploadStream(fileStream, fileSize, 4, {
        // Number of concurrent upload operations
        concurrency: 4,
        
        // Size of each block (4MB default, max 4000MB)
        bufferSize: 4 * 1024 * 1024,
        
        // Progress tracking
        onProgress: (progress) => {
          console.log(`Uploaded: ${progress.loadedBytes} bytes`);
        },
        
        // HTTP headers
        blobHTTPHeaders: {
          blobContentType: "application/zip",
          blobContentEncoding: "gzip",
        },
        
        // Custom metadata
        metadata: {
          uploadedBy: "my-app",
          version: "1.0",
        },
        
        // Access tier
        tier: "Cool",
      });
      ```
      
      ---
      
      ## Block Upload (Large Files)
      
      For large files, use staged block uploads for better control:
      
      ```typescript
      import { BlockBlobClient, BlockBlobStageBlockOptions } from "@azure/storage-blob";
      import { v4 as uuidv4 } from "uuid";
      
      async function uploadLargeFile(
        blobClient: BlockBlobClient,
        filePath: string,
        blockSize = 4 * 1024 * 1024 // 4MB blocks
      ) {
        const fileHandle = await fs.promises.open(filePath, "r");
        const fileStats = await fileHandle.stat();
        const fileSize = fileStats.size;
        
        const blockIds: string[] = [];
        let offset = 0;
        let blockIndex = 0;
        
        try {
          while (offset < fileSize) {
            const chunkSize = Math.min(blockSize, fileSize - offset);
            const buffer = Buffer.alloc(chunkSize);
            
            await fileHandle.read(buffer, 0, chunkSize, offset);
            
            // Generate block ID (must be base64 encoded, same length)
            const blockId = Buffer.from(
              blockIndex.toString().padStart(6, "0")
            ).toString("base64");
            
            // Stage the block
            await blobClient.stageBlock(blockId, buffer, buffer.length);
            
            blockIds.push(blockId);
            offset += chunkSize;
            blockIndex++;
            
            console.log(`Staged block ${blockIndex}, offset: ${offset}/${fileSize}`);
          }
          
          // Commit all blocks
          await blobClient.commitBlockList(blockIds, {
            blobHTTPHeaders: {
              blobContentType: "application/octet-stream",
            },
          });
          
          console.log("Upload complete!");
        } finally {
          await fileHandle.close();
        }
      }
      ```
      
      ---
      
      ## Parallel Upload/Download
      
      ### Parallel Download to File
      
      ```typescript
      import { BlobClient } from "@azure/storage-blob";
      
      // Downloads in parallel chunks automatically
      await blobClient.downloadToFile("./local-file.zip", 0, undefined, {
        // Chunk size for parallel download
        blockSize: 4 * 1024 * 1024,
        
        // Number of parallel downloads
        concurrency: 4,
        
        onProgress: (progress) => {
          console.log(`Downloaded: ${progress.loadedBytes} bytes`);
        },
      });
      ```
      
      ### Parallel Upload from File
      
      ```typescript
      // Uploads in parallel chunks automatically
      await blobClient.uploadFile("./large-file.zip", {
        // Chunk size
        blockSize: 4 * 1024 * 1024,
        
        // Parallel uploads
        concurrency: 4,
        
        onProgress: (progress) => {
          console.log(`Uploaded: ${progress.loadedBytes} bytes`);
        },
        
        blobHTTPHeaders: {
          blobContentType: "application/zip",
        },
      });
      ```
      
      ---
      
      ## Browser Upload
      
      For browser environments, use `uploadBrowserData`:
      
      ```typescript
      // From File input
      const fileInput = document.getElementById("file") as HTMLInputElement;
      const file = fileInput.files![0];
      
      await blobClient.uploadBrowserData(file, {
        onProgress: (progress) => {
          const percent = ((progress.loadedBytes / file.size) * 100).toFixed(2);
          document.getElementById("progress")!.textContent = `${percent}%`;
        },
        blobHTTPHeaders: {
          blobContentType: file.type,
        },
      });
      
      // From ArrayBuffer
      const arrayBuffer = await file.arrayBuffer();
      await blobClient.uploadBrowserData(arrayBuffer);
      
      // From Blob
      const blob = new Blob(["Hello, World!"], { type: "text/plain" });
      await blobClient.uploadBrowserData(blob);
      ```
      
      ---
      
      ## Abort Operations
      
      Cancel long-running uploads/downloads:
      
      ```typescript
      const abortController = new AbortController();
      
      // Set up abort after 30 seconds
      setTimeout(() => abortController.abort(), 30000);
      
      try {
        await blobClient.uploadFile("./large-file.zip", {
          abortSignal: abortController.signal,
          onProgress: (progress) => {
            console.log(`Progress: ${progress.loadedBytes}`);
          },
        });
      } catch (error) {
        if (error.name === "AbortError") {
          console.log("Upload was cancelled");
        } else {
          throw error;
        }
      }
      
      // Manual abort
      document.getElementById("cancel")?.addEventListener("click", () => {
        abortController.abort();
      });
      ```
      
      ---
      
      ## Copy Operations
      
      ### Copy from URL
      
      ```typescript
      const sourceUrl = "https://source-account.blob.core.windows.net/container/blob";
      
      // Start async copy
      const copyPoller = await blobClient.beginCopyFromURL(sourceUrl);
      
      // Wait for completion
      const result = await copyPoller.pollUntilDone();
      console.log(`Copy completed: ${result.copyStatus}`);
      
      // Or copy synchronously (for small blobs)
      await blobClient.syncCopyFromURL(sourceUrl);
      ```
      
      ### Copy with Progress
      
      ```typescript
      const copyPoller = await blobClient.beginCopyFromURL(sourceUrl, {
        onProgress: (state) => {
          if (state.copyProgress) {
            const [copied, total] = state.copyProgress.split("/").map(Number);
            console.log(`Copied: ${copied}/${total} bytes`);
          }
        },
      });
      ```
      
      ---
      
      ## Complete Example
      
      ```typescript
      import {
        BlobServiceClient,
        ContainerClient,
        BlockBlobClient,
      } from "@azure/storage-blob";
      import { DefaultAzureCredential } from "@azure/identity";
      import fs from "node:fs";
      import { pipeline } from "node:stream/promises";
      
      async function uploadAndDownload() {
        const credential = new DefaultAzureCredential();
        const blobServiceClient = new BlobServiceClient(
          `https://${process.env["STORAGE_ACCOUNT_NAME"]}.blob.core.windows.net`,
          credential
        );
      
        const containerClient = blobServiceClient.getContainerClient("demo");
        await containerClient.createIfNotExists();
      
        const blobClient = containerClient.getBlockBlobClient("test-file.txt");
      
        // Upload with progress
        console.log("Uploading...");
        const uploadData = Buffer.from("Hello, Azure Blob Storage!");
        await blobClient.upload(uploadData, uploadData.length, {
          onProgress: (progress) => {
            console.log(`Upload progress: ${progress.loadedBytes} bytes`);
          },
        });
        console.log("Upload complete!");
      
        // Download with progress
        console.log("Downloading...");
        const downloadResponse = await blobClient.download(0, undefined, {
          onProgress: (progress) => {
            console.log(`Download progress: ${progress.loadedBytes} bytes`);
          },
        });
      
        // Stream to file
        await pipeline(
          downloadResponse.readableStreamBody!,
          fs.createWriteStream("./downloaded-file.txt")
        );
        console.log("Download complete!");
      
        // Get blob properties
        const properties = await blobClient.getProperties();
        console.log(`Blob size: ${properties.contentLength} bytes`);
        console.log(`Content type: ${properties.contentType}`);
        console.log(`Last modified: ${properties.lastModified}`);
      
        // Cleanup
        await blobClient.delete();
        console.log("Blob deleted");
      }
      
      uploadAndDownload().catch(console.error);
      ```
      
      ---
      
      ## Best Practices
      
      1. **Use parallel operations** - `uploadFile`/`downloadToFile` automatically parallelize
      2. **Set appropriate block sizes** - 4-8MB for most scenarios, larger for huge files
      3. **Implement progress tracking** - Use `onProgress` for user feedback
      4. **Handle abort signals** - Allow users to cancel long operations
      5. **Set content types** - Always set `blobContentType` for proper browser handling
      6. **Use streams for large files** - Avoid loading entire files into memory
      7. **Implement retry logic** - SDK has built-in retries, configure as needed
      
      ---
      
      ## See Also
      
      - [SAS Token Patterns](./sas-tokens.md) - Generate secure access tokens
      - [Official Samples](https://github.com/Azure/azure-sdk-for-js/tree/main/sdk/storage/storage-blob/samples)
      
  • SKILL.md 12.8 KB
    ---
    name: azure-storage-blob-ts
    description: |
      Azure Blob Storage JavaScript/TypeScript SDK (@azure/storage-blob) for blob operations. Use for uploading, downloading, listing, and managing blobs and containers. Supports block blobs, append blobs, page blobs, SAS tokens, and streaming. Triggers: "blob storage", "@azure/storage-blob", "BlobServiceClient", "ContainerClient", "upload blob", "download blob", "SAS token", "block blob".
    license: MIT
    metadata:
      author: Microsoft
      version: "1.0.0"
      package: '@azure/storage-blob'
    ---
    
    # @azure/storage-blob (TypeScript/JavaScript)
    
    SDK for Azure Blob Storage operations — upload, download, list, and manage blobs and containers.
    
    ## Installation
    
    ```bash
    npm install @azure/storage-blob @azure/identity
    ```
    
    **Current Version**: 12.x  
    **Node.js**: >= 18.0.0
    
    ## Environment Variables
    
    ```bash
    AZURE_STORAGE_ACCOUNT_NAME=<account-name>
    AZURE_STORAGE_ACCOUNT_KEY=<account-key>
    # OR connection string
    AZURE_STORAGE_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=...
    AZURE_TOKEN_CREDENTIALS=prod # Required only if DefaultAzureCredential is used in production
    ```
    
    ## Authentication
    
    ### Microsoft Entra Token Credential (Recommended)
    
    ```typescript
    import { BlobServiceClient } from "@azure/storage-blob";
    import { DefaultAzureCredential, ManagedIdentityCredential } from "@azure/identity";
    
    // Local dev: DefaultAzureCredential. Production: set AZURE_TOKEN_CREDENTIALS=prod or AZURE_TOKEN_CREDENTIALS=<specific_credential>
    const credential = new DefaultAzureCredential({requiredEnvVars: ["AZURE_TOKEN_CREDENTIALS"]});
    // Or use a specific credential directly in production:
    // See https://learn.microsoft.com/javascript/api/overview/azure/identity-readme?view=azure-node-latest#credential-classes
    // const credential = new ManagedIdentityCredential();
    
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;
    const client = new BlobServiceClient(
      `https://${accountName}.blob.core.windows.net`,
      credential
    );
    ```
    
    ### Connection String
    
    ```typescript
    import { BlobServiceClient } from "@azure/storage-blob";
    
    const client = BlobServiceClient.fromConnectionString(
      process.env.AZURE_STORAGE_CONNECTION_STRING!
    );
    ```
    
    ### StorageSharedKeyCredential (Node.js only)
    
    ```typescript
    import { BlobServiceClient, StorageSharedKeyCredential } from "@azure/storage-blob";
    
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;
    const accountKey = process.env.AZURE_STORAGE_ACCOUNT_KEY!;
    
    const sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);
    const client = new BlobServiceClient(
      `https://${accountName}.blob.core.windows.net`,
      sharedKeyCredential
    );
    ```
    
    ### SAS Token
    
    ```typescript
    import { BlobServiceClient } from "@azure/storage-blob";
    
    const accountName = process.env.AZURE_STORAGE_ACCOUNT_NAME!;
    const sasToken = process.env.AZURE_STORAGE_SAS_TOKEN!; // starts with "?"
    
    const client = new BlobServiceClient(
      `https://${accountName}.blob.core.windows.net${sasToken}`
    );
    ```
    
    ## Client Hierarchy
    
    ```
    BlobServiceClient (account level)
    └── ContainerClient (container level)
        └── BlobClient (blob level)
            ├── BlockBlobClient (block blobs - most common)
            ├── AppendBlobClient (append-only blobs)
            └── PageBlobClient (page blobs - VHDs)
    ```
    
    ## Container Operations
    
    ### Create Container
    
    ```typescript
    const containerClient = client.getContainerClient("my-container");
    await containerClient.create();
    
    // Or create if not exists
    await containerClient.createIfNotExists();
    ```
    
    ### List Containers
    
    ```typescript
    for await (const container of client.listContainers()) {
      console.log(container.name);
    }
    
    // With prefix filter
    for await (const container of client.listContainers({ prefix: "logs-" })) {
      console.log(container.name);
    }
    ```
    
    ### Delete Container
    
    ```typescript
    await containerClient.delete();
    // Or delete if exists
    await containerClient.deleteIfExists();
    ```
    
    ## Blob Operations
    
    ### Upload Blob (Simple)
    
    ```typescript
    const containerClient = client.getContainerClient("my-container");
    const blockBlobClient = containerClient.getBlockBlobClient("my-file.txt");
    
    // Upload string
    await blockBlobClient.upload("Hello, World!", 13);
    
    // Upload Buffer
    const buffer = Buffer.from("Hello, World!");
    await blockBlobClient.upload(buffer, buffer.length);
    ```
    
    ### Upload from File (Node.js only)
    
    ```typescript
    const blockBlobClient = containerClient.getBlockBlobClient("uploaded-file.txt");
    await blockBlobClient.uploadFile("/path/to/local/file.txt");
    ```
    
    ### Upload from Stream (Node.js only)
    
    ```typescript
    import * as fs from "fs";
    
    const blockBlobClient = containerClient.getBlockBlobClient("streamed-file.txt");
    const readStream = fs.createReadStream("/path/to/local/file.txt");
    
    await blockBlobClient.uploadStream(readStream, 4 * 1024 * 1024, 5, {
      // bufferSize: 4MB, maxConcurrency: 5
      onProgress: (progress) => console.log(`Uploaded ${progress.loadedBytes} bytes`),
    });
    ```
    
    ### Upload from Browser
    
    ```typescript
    const blockBlobClient = containerClient.getBlockBlobClient("browser-upload.txt");
    
    // From File input
    const fileInput = document.getElementById("fileInput") as HTMLInputElement;
    const file = fileInput.files![0];
    await blockBlobClient.uploadData(file);
    
    // From Blob/ArrayBuffer
    const arrayBuffer = new ArrayBuffer(1024);
    await blockBlobClient.uploadData(arrayBuffer);
    ```
    
    ### Download Blob
    
    ```typescript
    const blobClient = containerClient.getBlobClient("my-file.txt");
    const downloadResponse = await blobClient.download();
    
    // Read as string (browser & Node.js)
    const downloaded = await streamToText(downloadResponse.readableStreamBody!);
    
    async function streamToText(readable: NodeJS.ReadableStream): Promise<string> {
      const chunks: Buffer[] = [];
      for await (const chunk of readable) {
        chunks.push(Buffer.from(chunk));
      }
      return Buffer.concat(chunks).toString("utf-8");
    }
    ```
    
    ### Download to File (Node.js only)
    
    ```typescript
    const blockBlobClient = containerClient.getBlockBlobClient("my-file.txt");
    await blockBlobClient.downloadToFile("/path/to/local/destination.txt");
    ```
    
    ### Download to Buffer (Node.js only)
    
    ```typescript
    const blockBlobClient = containerClient.getBlockBlobClient("my-file.txt");
    const buffer = await blockBlobClient.downloadToBuffer();
    console.log(buffer.toString());
    ```
    
    ### List Blobs
    
    ```typescript
    // List all blobs
    for await (const blob of containerClient.listBlobsFlat()) {
      console.log(blob.name, blob.properties.contentLength);
    }
    
    // List with prefix
    for await (const blob of containerClient.listBlobsFlat({ prefix: "logs/" })) {
      console.log(blob.name);
    }
    
    // List by hierarchy (virtual directories)
    for await (const item of containerClient.listBlobsByHierarchy("/")) {
      if (item.kind === "prefix") {
        console.log(`Directory: ${item.name}`);
      } else {
        console.log(`Blob: ${item.name}`);
      }
    }
    ```
    
    ### Delete Blob
    
    ```typescript
    const blobClient = containerClient.getBlobClient("my-file.txt");
    await blobClient.delete();
    
    // Delete if exists
    await blobClient.deleteIfExists();
    
    // Delete with snapshots
    await blobClient.delete({ deleteSnapshots: "include" });
    ```
    
    ### Copy Blob
    
    ```typescript
    const sourceBlobClient = containerClient.getBlobClient("source.txt");
    const destBlobClient = containerClient.getBlobClient("destination.txt");
    
    // Start copy operation
    const copyPoller = await destBlobClient.beginCopyFromURL(sourceBlobClient.url);
    await copyPoller.pollUntilDone();
    ```
    
    ## Blob Properties & Metadata
    
    ### Get Properties
    
    ```typescript
    const blobClient = containerClient.getBlobClient("my-file.txt");
    const properties = await blobClient.getProperties();
    
    console.log("Content-Type:", properties.contentType);
    console.log("Content-Length:", properties.contentLength);
    console.log("Last Modified:", properties.lastModified);
    console.log("ETag:", properties.etag);
    ```
    
    ### Set Metadata
    
    ```typescript
    await blobClient.setMetadata({
      author: "John Doe",
      category: "documents",
    });
    ```
    
    ### Set HTTP Headers
    
    ```typescript
    await blobClient.setHTTPHeaders({
      blobContentType: "text/plain",
      blobCacheControl: "max-age=3600",
      blobContentDisposition: "attachment; filename=download.txt",
    });
    ```
    
    ## SAS Token Generation (Node.js only)
    
    ### Generate Blob SAS
    
    ```typescript
    import {
      BlobSASPermissions,
      generateBlobSASQueryParameters,
      StorageSharedKeyCredential,
    } from "@azure/storage-blob";
    
    const sharedKeyCredential = new StorageSharedKeyCredential(accountName, accountKey);
    
    const sasToken = generateBlobSASQueryParameters(
      {
        containerName: "my-container",
        blobName: "my-file.txt",
        permissions: BlobSASPermissions.parse("r"), // read only
        startsOn: new Date(),
        expiresOn: new Date(Date.now() + 3600 * 1000), // 1 hour
      },
      sharedKeyCredential
    ).toString();
    
    const sasUrl = `https://${accountName}.blob.core.windows.net/my-container/my-file.txt?${sasToken}`;
    ```
    
    ### Generate Container SAS
    
    ```typescript
    import { ContainerSASPermissions, generateBlobSASQueryParameters } from "@azure/storage-blob";
    
    const sasToken = generateBlobSASQueryParameters(
      {
        containerName: "my-container",
        permissions: ContainerSASPermissions.parse("racwdl"), // read, add, create, write, delete, list
        expiresOn: new Date(Date.now() + 24 * 3600 * 1000), // 24 hours
      },
      sharedKeyCredential
    ).toString();
    ```
    
    ### Generate Account SAS
    
    ```typescript
    import {
      AccountSASPermissions,
      AccountSASResourceTypes,
      AccountSASServices,
      generateAccountSASQueryParameters,
    } from "@azure/storage-blob";
    
    const sasToken = generateAccountSASQueryParameters(
      {
        services: AccountSASServices.parse("b").toString(), // blob
        resourceTypes: AccountSASResourceTypes.parse("sco").toString(), // service, container, object
        permissions: AccountSASPermissions.parse("rwdlacupi"), // all permissions
        expiresOn: new Date(Date.now() + 24 * 3600 * 1000),
      },
      sharedKeyCredential
    ).toString();
    ```
    
    ## Blob Types
    
    ### Block Blob (Default)
    
    Most common type for text and binary files.
    
    ```typescript
    const blockBlobClient = containerClient.getBlockBlobClient("document.pdf");
    await blockBlobClient.uploadFile("/path/to/document.pdf");
    ```
    
    ### Append Blob
    
    Optimized for append operations (logs, audit trails).
    
    ```typescript
    const appendBlobClient = containerClient.getAppendBlobClient("app.log");
    
    // Create the append blob
    await appendBlobClient.create();
    
    // Append data
    await appendBlobClient.appendBlock("Log entry 1\n", 12);
    await appendBlobClient.appendBlock("Log entry 2\n", 12);
    ```
    
    ### Page Blob
    
    Fixed-size blobs for random read/write (VHDs).
    
    ```typescript
    const pageBlobClient = containerClient.getPageBlobClient("disk.vhd");
    
    // Create 512-byte aligned page blob
    await pageBlobClient.create(1024 * 1024); // 1MB
    
    // Write pages (must be 512-byte aligned)
    const buffer = Buffer.alloc(512);
    await pageBlobClient.uploadPages(buffer, 0, 512);
    ```
    
    ## Error Handling
    
    ```typescript
    import { RestError } from "@azure/storage-blob";
    
    try {
      await containerClient.create();
    } catch (error) {
      if (error instanceof RestError) {
        switch (error.statusCode) {
          case 404:
            console.log("Container not found");
            break;
          case 409:
            console.log("Container already exists");
            break;
          case 403:
            console.log("Access denied");
            break;
          default:
            console.error(`Storage error ${error.statusCode}: ${error.message}`);
        }
      }
      throw error;
    }
    ```
    
    ## TypeScript Types Reference
    
    ```typescript
    import {
      // Clients
      BlobServiceClient,
      ContainerClient,
      BlobClient,
      BlockBlobClient,
      AppendBlobClient,
      PageBlobClient,
    
      // Authentication
      StorageSharedKeyCredential,
      AnonymousCredential,
    
      // SAS
      BlobSASPermissions,
      ContainerSASPermissions,
      AccountSASPermissions,
      AccountSASServices,
      AccountSASResourceTypes,
      generateBlobSASQueryParameters,
      generateAccountSASQueryParameters,
    
      // Options & Responses
      BlobDownloadResponseParsed,
      BlobUploadCommonResponse,
      ContainerCreateResponse,
      BlobItem,
      ContainerItem,
    
      // Errors
      RestError,
    } from "@azure/storage-blob";
    ```
    
    ## Best Practices
    
    1. **Use `DefaultAzureCredential` for local development; use `ManagedIdentityCredential` or `WorkloadIdentityCredential` for production**
    2. **Use streaming for large files** — `uploadStream`/`downloadToFile` for files > 256MB
    3. **Set appropriate content types** — Use `setHTTPHeaders` for correct MIME types
    4. **Use SAS tokens for client access** — Generate short-lived tokens for browser uploads
    5. **Handle errors gracefully** — Check `RestError.statusCode` for specific handling
    6. **Use `*IfNotExists` methods** — For idempotent container/blob creation
    7. **Close clients** — Not required but good practice in long-running apps
    
    ## Platform Differences
    
    | Feature | Node.js | Browser |
    |---------|---------|---------|
    | `StorageSharedKeyCredential` | ✅ | ❌ |
    | `uploadFile()` | ✅ | ❌ |
    | `uploadStream()` | ✅ | ❌ |
    | `downloadToFile()` | ✅ | ❌ |
    | `downloadToBuffer()` | ✅ | ❌ |
    | `uploadData()` | ✅ | ✅ |
    | SAS generation | ✅ | ❌ |
    | DefaultAzureCredential | ✅ | ❌ |
    | Anonymous/SAS access | ✅ | ✅ |
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related