Using Azure File Storage with Go

Getting Started with Azure Files in Go

This guide will walk you through the process of interacting with Azure File Storage using the Go programming language. Azure Files offers fully managed cloud file shares that are accessible via the industry-standard Server Message Block (SMB) protocol.

Prerequisites

  • An Azure account with an active subscription.
  • A storage account in Azure.
  • Go installed on your system.
  • The Azure SDK for Go.

Installing the Azure SDK for Go

You can install the necessary Azure SDK packages for Go using the go get command:

go get github.com/Azure/azure-sdk-for-go/sdk/storage/azblob
go get github.com/Azure/azure-sdk-for-go/sdk/storage/azfile

Connecting to Azure Files

To interact with Azure Files, you need to create a client. This typically involves providing your storage account name and a shared access signature (SAS) token or an account key.

Using Account Key:

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/file"
)

func main() {
    accountName := os.Getenv("AZURE_STORAGE_ACCOUNT_NAME")
    accountKey := os.Getenv("AZURE_STORAGE_ACCOUNT_KEY")
    shareName := "myfileshare" // Replace with your share name

    if accountName == "" || accountKey == "" {
        log.Fatal("Please set AZURE_STORAGE_ACCOUNT_NAME and AZURE_STORAGE_ACCOUNT_KEY environment variables.")
    }

    // Create a file client using the account key
    client, err := file.NewClient(accountName, file.NewSharedKeyCredential(accountName, accountKey), nil)
    if err != nil {
        log.Fatalf("Failed to create file client: %v", err)
    }

    ctx := context.Background()

    // Example: List shares (optional)
    sharesPager := client.NewListSharesPager(nil)
    fmt.Println("Shares in the storage account:")
    for sharesPager.More() {
        page, err := sharesPager.NextPage(ctx)
        if err != nil {
            log.Fatalf("Failed to advance page for shares: %v", err)
        }
        for _, share := range page.Shares {
            fmt.Printf("- %s\n", *share.Name)
        }
    }

    // Get a client for a specific share
    shareClient := client.NewShareClient(shareName)
    fmt.Printf("Successfully obtained client for share: %s\n", shareName)
}

Using SAS Token:

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/file"
)

func main() {
    accountName := os.Getenv("AZURE_STORAGE_ACCOUNT_NAME")
    sasToken := os.Getenv("AZURE_STORAGE_SAS_TOKEN") // e.g., "?sv=2020-08-04&ss=bfqt&srt=sco&sp=rwdlacupx..."
    shareName := "myfileshare" // Replace with your share name

    if accountName == "" || sasToken == "" {
        log.Fatal("Please set AZURE_STORAGE_ACCOUNT_NAME and AZURE_STORAGE_SAS_TOKEN environment variables.")
    }

    // Create a file client using the SAS token
    // Note: The SAS token should include the leading '?'
    client, err := file.NewClient(accountName, file.NewAnonymousClient(sasToken), nil)
    if err != nil {
        log.Fatalf("Failed to create file client: %v", err)
    }

    ctx := context.Background()
    shareClient := client.NewShareClient(shareName)
    fmt.Printf("Successfully obtained client for share: %s using SAS token\n", shareName)
}
Make sure to secure your account keys and SAS tokens. Do not hardcode them directly in your source code for production applications. Use environment variables or a secrets management service.

Working with Directories and Files

Once you have a share client, you can manage directories and files within that share.

Creating a Directory

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/file"
)

func main() {
    accountName := os.Getenv("AZURE_STORAGE_ACCOUNT_NAME")
    accountKey := os.Getenv("AZURE_STORAGE_ACCOUNT_KEY")
    shareName := "myfileshare"
    directoryName := "mygo-directory"

    client, err := file.NewClient(accountName, file.NewSharedKeyCredential(accountName, accountKey), nil)
    if err != nil {
        log.Fatalf("Failed to create file client: %v", err)
    }
    shareClient := client.NewShareClient(shareName)
    dirClient := shareClient.NewDirectoryClient(directoryName)

    ctx := context.Background()

    _, err = dirClient.Create(ctx, nil)
    if err != nil {
        log.Fatalf("Failed to create directory: %v", err)
    }
    fmt.Printf("Directory '%s' created successfully in share '%s'.\n", directoryName, shareName)
}

Uploading a File

You can upload files using various methods. For simplicity, we'll use a direct upload. For larger files, consider resumable uploads.

import (
    "context"
    "fmt"
    "log"
    "os"
    "strings"

    "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/file"
)

func main() {
    accountName := os.Getenv("AZURE_STORAGE_ACCOUNT_NAME")
    accountKey := os.Getenv("AZURE_STORAGE_ACCOUNT_KEY")
    shareName := "myfileshare"
    directoryName := "mygo-directory"
    fileName := "hello.txt"
    fileContent := "Hello from Go and Azure Files!"

    client, err := file.NewClient(accountName, file.NewSharedKeyCredential(accountName, accountKey), nil)
    if err != nil {
        log.Fatalf("Failed to create file client: %v", err)
    }
    shareClient := client.NewShareClient(shareName)
    dirClient := shareClient.NewDirectoryClient(directoryName)
    fileClient := dirClient.NewFileClient(fileName)

    ctx := context.Background()

    // Upload the file content
    _, err = fileClient.UploadBuffer(ctx, []byte(fileContent), nil)
    if err != nil {
        log.Fatalf("Failed to upload file: %v", err)
    }
    fmt.Printf("File '%s' uploaded successfully to '%s/%s'.\n", fileName, directoryName, shareName)
}

Downloading a File

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/Azure/azure-sdk-for-go/sdk/storage/azfile/file"
)

func main() {
    accountName := os.Getenv("AZURE_STORAGE_ACCOUNT_NAME")
    accountKey := os.Getenv("AZURE_STORAGE_ACCOUNT_KEY")
    shareName := "myfileshare"
    directoryName := "mygo-directory"
    fileName := "hello.txt"
    localDownloadPath := "./downloaded-hello.txt"

    client, err := file.NewClient(accountName, file.NewSharedKeyCredential(accountName, accountKey), nil)
    if err != nil {
        log.Fatalf("Failed to create file client: %v", err)
    }
    shareClient := client.NewShareClient(shareName)
    dirClient := shareClient.NewDirectoryClient(directoryName)
    fileClient := dirClient.NewFileClient(fileName)

    ctx := context.Background()

    // Download the file content
    resp, err := fileClient.DownloadStream(ctx, nil)
    if err != nil {
        log.Fatalf("Failed to download file: %v", err)
    }
    defer resp.Body.Close()

    // You can read the content directly or write to a file
    // For simplicity, we'll print it here
    content := make([]byte, resp.ContentLength)
    _, err = resp.Body.Read(content)
    if err != nil {
        log.Fatalf("Failed to read downloaded content: %v", err)
    }

    fmt.Printf("Downloaded content of '%s':\n%s\n", fileName, string(content))

    // Optionally, write to a local file
    // err = os.WriteFile(localDownloadPath, content, 0644)
    // if err != nil {
    //     log.Fatalf("Failed to write to local file: %v", err)
    // }
    // fmt.Printf("File downloaded and saved to: %s\n", localDownloadPath)
}
Large File Uploads: For files larger than a few megabytes, consider using the UploadFile function which handles chunking and retries automatically.

Error Handling

Always check for errors after each SDK operation. The Azure SDK for Go returns detailed error information that can help you diagnose issues.

Next Steps

Explore more advanced features such as:

  • Setting file/directory properties (e.g., content type, metadata).
  • Deleting files and directories.
  • Listing contents of a directory.
  • Working with snapshots.
  • Implementing resumable uploads for very large files.

Refer to the official Azure SDK for Go documentation for comprehensive details and examples.