Table of Contents

Downloading Files

Download images and models with customizable path patterns, hash verification, and format detection.

Service Registration

Default:

// Uses system temp directory with minimal patterns
builder.Services.AddCivitaiDownloads();

From Config:

builder.Services.AddCivitaiDownloads(builder.Configuration.GetSection("CivitaiDownloads"));

Programmatic:

builder.Services.AddCivitaiDownloads(options =>
{
    options.Images.BaseDirectory = @"C:\Downloads\Images";
    options.Images.PathPattern = "{Username}/{Id}.{Extension}";
    options.Images.OverwriteExisting = false;
    
    options.Models.BaseDirectory = @"C:\Models";
    options.Models.PathPattern = "{ModelType}/{ModelName}/{FileName}";
    options.Models.OverwriteExisting = true;
    options.Models.VerifyHash = true;
    options.Models.HashAlgorithm = HashAlgorithm.Sha256;
});

Download Images

Basic:

// Download an image using configured options
var imageResult = await apiClient.Images
    .ExecuteAsync(resultsLimit: 1);

if (imageResult is Result<PagedResult<Image>>.Success imageSuccess)
{
    var image = imageSuccess.Data.Items.FirstOrDefault();
    if (image is not null)
    {
        var downloadResult = await downloadService.DownloadAsync(image);
        
        if (downloadResult is Result<DownloadedFile>.Success downloadSuccess)
        {
            Console.WriteLine($"Downloaded to: {downloadSuccess.Data.FilePath}");
            Console.WriteLine($"Size: {downloadSuccess.Data.SizeBytes:N0} bytes");
        }
    }
}

Custom Directory:

// Download an image to a custom directory
var imageCustomResult = await apiClient.Images
    .WhereUsername("Mewyk")
    .ExecuteAsync(resultsLimit: 1);

if (imageCustomResult is Result<PagedResult<Image>>.Success customPathSuccess)
{
    var image = customPathSuccess.Data.Items.FirstOrDefault();
    if (image is not null)
    {
        var downloadResult = await downloadService.DownloadAsync(
            image,
            @"D:\CustomImages");
        
        if (downloadResult is Result<DownloadedFile>.Success downloadSuccess)
        {
            Console.WriteLine($"Downloaded to custom path: {downloadSuccess.Data.FilePath}");
        }
    }
}

Image Path Tokens

Tokens are replaced by the service at download time:

{Id}, {PostId}, {Username}, {Width}, {Height}, {BaseModel}, {NsfwLevel}, {Date}, {Extension}

Example:

{
  "PathPattern": "{BaseModel}/{Username}/{Date}_{Id}.{Extension}"
}

Result: SDXL 1.0/ArtistName/2026-01-15_12345678.png

Download Models

Basic:

// Download a model file (without version context)
var modelFileResult = await apiClient.Models.GetByIdAsync(4201);

if (modelFileResult is Result<Model>.Success modelFileSuccess)
{
    var version = modelFileSuccess.Data.ModelVersions?.FirstOrDefault();
    var file = version?.Files?.FirstOrDefault(f => f.Primary == true);
    
    if (file is not null)
    {
        var downloadResult = await downloadService.DownloadAsync(file);
        
        if (downloadResult is Result<DownloadedFile>.Success downloadSuccess)
        {
            Console.WriteLine($"Downloaded: {downloadSuccess.Data.FilePath}");
            Console.WriteLine($"Verified: {downloadSuccess.Data.IsVerified}");
        }
    }
}

With Version:

// Download a model file with version context for better path organization
var modelVersionResult = await apiClient.Models.GetByIdAsync(4201);

if (modelVersionResult is Result<Model>.Success modelVersionSuccess)
{
    var version = modelVersionSuccess.Data.ModelVersions?.FirstOrDefault();
    var file = version?.Files?.FirstOrDefault(f => f.Primary == true);
    
    if (file is not null && version is not null)
    {
        // Providing version enables additional path tokens like {ModelName}, {BaseModel}, etc.
        var downloadResult = await downloadService.DownloadAsync(file, version);
        
        if (downloadResult is Result<DownloadedFile>.Success downloadSuccess)
        {
            Console.WriteLine($"Downloaded: {downloadSuccess.Data.FilePath}");
            Console.WriteLine($"Size: {downloadSuccess.Data.SizeBytes:N0} bytes");
            Console.WriteLine($"Verified: {downloadSuccess.Data.IsVerified}");
            if (downloadSuccess.Data.ComputedHash is not null)
            {
                Console.WriteLine($"Hash: {downloadSuccess.Data.ComputedHash}");
            }
        }
        else if (downloadResult is Result<DownloadedFile>.Failure downloadFailure)
        {
            Console.WriteLine($"Download failed: {downloadFailure.Error.Message}");
        }
    }
}

From URL:

// Download from a raw URL with hash verification
var rawUrl = "https://civitai.com/api/download/models/130072";
var destinationPath = @"C:\Downloads\model.safetensors";
var expectedHash = "abc123def456...";

var urlResult = await downloadService.DownloadAsync(
    rawUrl,
    destinationPath,
    expectedHash,
    HashAlgorithm.Sha256);

if (urlResult is Result<DownloadedFile>.Success urlDownloadSuccess)
{
    Console.WriteLine($"Downloaded and verified: {urlDownloadSuccess.Data.FilePath}");
}

Model Path Tokens

File tokens: {FileId}, {FileName}, {FileType}, {Format}, {Size}, {Precision}

Version tokens: {VersionId}, {VersionName}, {BaseModel}, {ModelId}, {ModelName}, {ModelType}

Example:

{
  "PathPattern": "{ModelType}/{BaseModel}/{ModelName}/{VersionName}/{FileName}"
}

Hash Verification

builder.Services.AddCivitaiDownloads(options =>
{
    options.Models.VerifyHash = true;
    options.Models.HashAlgorithm = HashAlgorithm.Sha256;
});

Automatically downloads, computes hash, compares against Civitai metadata, and deletes on mismatch.

Verification Result:

var verifyModelResult = await apiClient.Models.GetByIdAsync(4201);
if (verifyModelResult is Result<Model>.Success verifyModelSuccess)
{
    var version = verifyModelSuccess.Data.ModelVersions?.FirstOrDefault();
    var file = version?.Files?.FirstOrDefault(f => f.Primary == true);
    
    if (file is not null && version is not null)
    {
        var verifyResult = await downloadService.DownloadAsync(file, version);

        if (verifyResult is Result<DownloadedFile>.Success verifySuccess)
        {
            if (verifySuccess.Data.IsVerified)
            {
                Console.WriteLine($"Verified hash: {verifySuccess.Data.ComputedHash}");
            }
            else
            {
                Console.WriteLine("Hash verification was not performed");
            }
        }
    }
}

File Format Detection

// Detect from file path
var format = await FileFormatDetector.DetectFormatAsync(filePath);
Console.WriteLine($"Detected format: {format}"); // "png", "jpg", "mp4", etc.

// Detect from stream
await using var formatStream = File.OpenRead(filePath);
var formatFromStream = await FileFormatDetector.DetectFormatAsync(formatStream);

// Detect from bytes
var header = new byte[16];
var bytesRead = await formatStream.ReadAsync(header.AsMemory());
var formatFromBytes = FileFormatDetector.DetectFormat(header.AsSpan(0, bytesRead));

Supported: PNG, JPEG, GIF, WebP, AVIF, HEIC, MP4, WebM

Error Handling

Codes: ImageUrlMissing, DownloadUrlMissing, HashVerificationFailed, HashComputationFailed

var errorModelResult = await apiClient.Models.GetByIdAsync(4201);
if (errorModelResult is Result<Model>.Success errorModelSuccess)
{
    var version = errorModelSuccess.Data.ModelVersions?.FirstOrDefault();
    var file = version?.Files?.FirstOrDefault(f => f.Primary == true);
    
    if (file is not null && version is not null)
    {
        var errorResult = await downloadService.DownloadAsync(file, version);

        switch (errorResult)
        {
            case Result<DownloadedFile>.Success success:
                Console.WriteLine($"Downloaded: {success.Data.FilePath}");
                break;
                
            case Result<DownloadedFile>.Failure { Error.Code: ErrorCode.HashVerificationFailed } failure:
                Console.WriteLine($"Corrupted download: {failure.Error.Message}");
                break;
                
            case Result<DownloadedFile>.Failure { Error.Code: ErrorCode.IoError } failure:
                Console.WriteLine($"File exists: {failure.Error.Message}");
                break;
                
            case Result<DownloadedFile>.Failure failure:
                Console.WriteLine($"Download failed: {failure.Error.Message}");
                break;
        }
    }
}

Configuration

ImageDownloadOptions:

  • BaseDirectory - Root directory
  • PathPattern - Pattern with tokens
  • OverwriteExisting - Replace files

ModelDownloadOptions:

  • BaseDirectory - Root directory
  • PathPattern - Pattern with tokens
  • OverwriteExisting - Replace files
  • VerifyHash - Verify after download
  • HashAlgorithm - Algorithm for verification

Next Steps