Ofload images to blob storage and update images

I’m hitting some limits on my site and I would like to offload the images to a dedicated blob storage account.

I want to see if there is any help in Hugo for this scenario!

Here is my plan:

  1. After building the Hugo Site, I’ll sync all images using azcopy sync against public
  2. Delete all images from public
  3. Update all links in all HTML files in public to be /blob/bla/bla.jpg instead of /bla/bla.jpg

Im using Azure Front Door so I can map https://example.com/blob/* to the blob storage location…

I feel like the links update in the files would be a lot of processing. Unless I somehow get hugo to do it at render time. All of the images are part of page bundles, but they may be in frontmatter or in *.md content.

not clear for me if you want to migrate your sources or do it every time.

if you do it everytime the frontmatter fe is not relevant.

the simplest I can think of is to do something like that in alll code where you render images shortcodes, image-render-hook, …

{{ strings.Replace $image.Permalink "https://www.example.com/blob" BLOBLINK }}

ofc the images won’t work locally. so you might want do that with env=production and keep the old link with env=development

a postprocessing script replacing that string in all html files recursively will be quite simple which will keep the local stuff running and is only called on deployment.

1 Like

You are right.

I think im going to go with a rewrite at build time… that way it does not affect local…

My POC runs against ./public/

# Variables
$LocalImagesPath = ".\public\"  # Local folder containing images and HTML files
$BlobUrlBase = "https://nkdagilityblobs.blob.core.windows.net/`$web"  # Base URL for the blob storage
$BlobPath = "blob"  # Path to be added for images in HTML
$LocalDebug = $true

# Method 1: Upload image files using azcopy
function Upload-ImageFiles {
    param (
        [string]$LocalPath,
        [string]$AzureSASToken
    )
    try {
        Write-Host "Uploading image files to Azure Blob Storage using azcopy..."
        azcopy sync $LocalPath "$BlobUrlBase$AzureSASToken" --recursive --include-pattern "*.jpg;*.jpeg;*.png;*.gif;*.webp"
        Write-Host "Upload complete."
    }
    catch {
        Write-Host "Error during upload: $_"
    }
}

# Method 2: Delete all image files locally
function Delete-LocalImageFiles {
    param (
        [string]$LocalPath
    )
    try {
        Write-Host "Deleting all image files locally..."
        Get-ChildItem -Path $LocalPath -Recurse -Include *.jpg, *.jpeg, *.png, *.gif, *.webp | ForEach-Object {
            try {
                Remove-Item -Path $_.FullName -Force
                Write-Host "Deleted: $($_.FullName)"
            }
            catch {
                Write-Host "Error deleting file $($_.FullName): $_"
            }
        }
    }
    catch {
        Write-Host "Error during file deletion: $_"
    }
}

# Method 3: Rewrite image links in .html files using regex
function Rewrite-ImageLinks {
    param (
        [string]$LocalPath,
        [string]$BlobPath,
        [string]$BlobUrlBase   )
    try {
        Write-Host "Rewriting image links in .html files using regex..."
        if ($Debug) {
            Write-Host "DEBUG MODE: All paths will use BlobUrlBase ($BlobUrlBase)"
        }

        $HtmlFiles = Get-ChildItem -Path $LocalPath -Recurse -Include *.html

        foreach ($HtmlFile in $HtmlFiles) {
            try {
                $FileContent = Get-Content -Path (Resolve-Path $HtmlFile.FullName) -Raw

                # Regex to match all src attributes with image paths
                $ImageRegex = 'src=["'']([^"'']*\.(jpg|jpeg|png|gif|webp))["'']'
                # Find all matches
                $Matches = [regex]::Matches($FileContent, $ImageRegex)

                foreach ($Match in $Matches) {
                    $OriginalPath = $Match.Groups[1].Value
                    $Extension = $Match.Groups[2].Value

                    # Skip if the path already contains 'blob' unless in debug mode
                    if ($OriginalPath -notmatch 'blob/') {
                        # Determine the updated path
                        $UpdatedPath = $OriginalPath

                        if ($OriginalPath.StartsWith("https://nkdagility.com") -or 
                            $OriginalPath.StartsWith("https://preview.nkdagility.com") -or 
                            $OriginalPath -match "^https:\/\/(yellow-pond-042d21b03|[a-z0-9-]+)\.azurestaticapps\.net") {
                            # Add '/blob/' to supported domains
                            $UpdatedPath = $OriginalPath -replace "^(https?:\/\/.*?)(\/|\/\/)", "`$1/$BlobPath/"
                        }
                        elseif ($OriginalPath.StartsWith("/")) {
                            # Absolute path
                            $UpdatedPath = "/$BlobPath" + $OriginalPath
                        }
                        elseif ($OriginalPath.StartsWith("./")) {
                            # Relative path
                            $UpdatedPath = "./$BlobPath" + $OriginalPath.Substring(1)
                        }

                        # Replace the original path in the content
                        $FileContent = $FileContent -replace [regex]::Escape($OriginalPath), $UpdatedPath
                        Write-Host "Replaced: $OriginalPath -> $UpdatedPath"
                    }
                }

                # Save updated content back to the file
                Set-Content -Path $HtmlFile.FullName -Value $FileContent
                Write-Host "Updated: $($HtmlFile.FullName)"
            }
            catch {
                Write-Host "Error processing file $($HtmlFile.FullName): $_"
            }
        }
        Write-Host "HTML link rewriting complete."
    }
    catch {
        Write-Host "Error during HTML processing: $_"
    }
}


cls

# Main Execution
$AzureSASToken = $env:AZURE_BLOB_STORAGE_SAS_TOKEN  # Environment variable for SAS token

Write-Host "Starting process..."
Upload-ImageFiles -LocalPath $LocalImagesPath -AzureSASToken $AzureSASToken
Delete-LocalImageFiles -LocalPath $LocalImagesPath

#Rewrite-ImageLinks -LocalPath $LocalImagesPath -BlobPath $BlobPath
Write-Host "Process complete!"

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.