Skip to main content
VisionaryAI uses Azure Blob Storage to persist generated DALL-E images. This guide explains the storage architecture and SAS token authentication.

Storage architecture

The application uses a single storage container called images to store all generated artwork. Each image is:
  • Named with the format: {prompt}_{timestamp}.png
  • Stored as a PNG file (1024x1024 pixels)
  • Secured with SAS (Shared Access Signature) tokens
  • Sorted by timestamp for chronological display

Container configuration

The images container is configured with:
  • Public access: Disabled (private)
  • Authentication: SAS tokens required
  • Permissions: Read, write, and create via SAS tokens
Never enable public blob access. All image access should go through SAS tokens to maintain security and control.

SAS token generation

The application generates short-lived SAS tokens to access blob storage securely.

Implementation

Here’s how VisionaryAI generates SAS tokens:
azure/lib/generateSASToken.js
const {
    BlobServiceClient,
    StorageSharedKeyCredential,
    BlobSASPermissions,
    generateBlobSASQueryParameters,
} = require('@azure/storage-blob');

const accountName = process.env.accountName;
const accountKey = process.env.accountKey;
const containerName = "images";

const sharedKeyCredential = new StorageSharedKeyCredential(
    accountName,
    accountKey
)

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

async function generateSASToken() {
    const containerClient = blobServiceClient.getContainerClient(containerName);

    const permissions = new BlobSASPermissions();
    permissions.write = true;
    permissions.read = true;
    permissions.create = true;

    const expiryDate = new Date();
    expiryDate.setMinutes(expiryDate.getMinutes() + 30);

    const sasToken = generateBlobSASQueryParameters({
        containerName: containerClient.containerName,
        permissions: permissions.toString(),
        expiresOn: expiryDate,
    },
        sharedKeyCredential
    ).toString();

    return sasToken;
}

module.exports = generateSASToken;

Key features

1

Credential initialization

Creates a StorageSharedKeyCredential using the storage account name and key from environment variables.
2

Permission configuration

Sets up SAS permissions:
  • Read: View existing images
  • Write: Modify images (if needed)
  • Create: Upload new images
3

Token expiration

Tokens expire 30 minutes after generation. This provides:
  • Security through short-lived credentials
  • Sufficient time for image upload and retrieval
  • Automatic revocation without manual intervention
SAS tokens are regenerated for each request, ensuring fresh credentials and minimizing security risks.

Image upload workflow

When a user generates an image, the following process occurs:
1

DALL-E generates the image

OpenAI’s DALL-E 3 API creates the image and returns a temporary URL.
2

Download image data

The function downloads the image as an array buffer:
const res = await axios.get(image_url, { responseType: 'arraybuffer' });
const arrayBuffer = res.data;
3

Generate SAS token

A fresh SAS token is generated with write permissions:
const sasToken = await generateSASToken();
4

Upload to blob storage

The image is uploaded using the blob client:
const blobServiceClient = new BlobServiceClient(
    `https://${accountName}.blob.core.windows.net?${sasToken}`
);
const containerClient = blobServiceClient.getContainerClient(containerName);
const timestamp = new Date().getTime();
const file_name = `${prompt}_${timestamp}.png`;
const blockBlobClient = containerClient.getBlockBlobClient(file_name);

await blockBlobClient.uploadData(arrayBuffer);

Image retrieval

The getImages function retrieves all images from the container with SAS-authenticated URLs.

Implementation

azure/src/functions/getImages.js
const { app } = require('@azure/functions');
const { BlobServiceClient, StorageSharedKeyCredential } = require('@azure/storage-blob');
const generateSASToken = require('../../lib/generateSASToken');

const accountName = process.env.accountName;
const accountKey = process.env.accountKey;
const containerName = "images";

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

app.http("getImages", {
    methods: ["GET"],
    authLevel: "anonymous",
    handler: async (request, context) => {
        const containerClient = blobServiceClient.getContainerClient(containerName);
        const imageUrls = [];
        const sasToken = await generateSASToken();

        for await (const blob of containerClient.listBlobsFlat()) {
            const imageUrl = `${blob.name}?${sasToken}`;
            const url = `https://${accountName}.blob.core.windows.net/${containerName}/${imageUrl}`;
            imageUrls.push({ url, name: blob.name });
        }
        
        const sortedImageUrls = imageUrls.sort((a, b) => {
            const aName = a.name.split("_").pop().toString().split(".").shift();
            const bName = b.name.split("_").pop().toString().split(".").shift();
            return bName - aName; 
        });
        
        return {
            jsonBody: {
                imageUrls: sortedImageUrls
            }
        }
    }
});

Sorting logic

Images are sorted by timestamp (newest first):
  1. Extract the timestamp from the filename: prompt_1234567890.png1234567890
  2. Sort in descending order (newest first)
  3. Return the sorted array with authenticated URLs

Storage credentials

Two credentials are required for blob storage operations:
accountName=your-storage-account-name
Retrieve these from the Azure Portal:
  1. Navigate to your Storage Account
  2. Select Access keys under Security + networking
  3. Copy Storage account name and Key (key1 or key2)
Store these credentials securely in your Function App’s application settings. Never commit them to source control.

Best practices

Security

  • Use SAS tokens instead of making the container public
  • Set appropriate token expiration times (30 minutes recommended)
  • Rotate storage account keys periodically
  • Use Azure Key Vault for production deployments

Performance

  • Use array buffers for efficient binary data handling
  • Generate SAS tokens per-request to avoid expiration issues
  • Consider implementing caching for frequently accessed images

Cost optimization

  • Use Standard tier (LRS replication) for cost-effective storage
  • Implement lifecycle policies to archive old images
  • Monitor storage usage in Azure Portal

Troubleshooting

”Image upload failed” error

Cause: SAS token may have expired or insufficient permissions. Solution:
  • Verify token expiration time
  • Ensure write and create permissions are enabled
  • Check storage account credentials
Cause: SAS token in image URLs may have expired. Solution:
  • Regenerate SAS tokens when fetching images
  • Ensure the getImages function generates fresh tokens

Authentication errors

Cause: Invalid storage account credentials. Solution:
  • Verify accountName and accountKey environment variables
  • Check that the credentials match your storage account
  • Regenerate access keys if compromised

Next steps

Build docs developers (and LLMs) love