Public/WebTools/Start-DownloadWithRetry.ps1

function Start-DownloadWithRetry {
  <#
    .SYNOPSIS
      Downloads a file from a specified Uri with retries.
 
    .DESCRIPTION
      The Start-DownloadWithRetry cmdlet attempts to download a file from the specified Uri to a local path.
      It includes retry logic for handling transient failures, allowing you to specify the maximum number of retries and the delay between attempts.
 
    .EXAMPLE
      $d = Start-DownloadWithRetry -Uri "https://pastebin.com/raw/JVciSv1S"
      cat $d.FullName | Write-Console -f Red
 
    .EXAMPLE
      $outfiles = @()
      $baseUri = 'https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/720'
      @(
        "$baseUri/Big_Buck_Bunny_720_10s_1MB.mp4"
        "$baseUri/Big_Buck_Bunny_720_10s_2MB.mp4"
        "$baseUri/Big_Buck_Bunny_720_10s_5MB.mp4"
        "$baseUri/Big_Buck_Bunny_720_10s_10MB.mp4"
      ) | % { $outfiles += Start-DownloadWithRetry $_ }
      $outfiles
 
    .EXAMPLE
      Start-DownloadWithRetry -Uri "https://test-videos.co.uk/vids/bigbuckbunny/mp4/h264/720/Big_Buck_Bunny_720_10s_5MB.mp4" -Name "mysamplevideo.mp4" -DownloadPath $pwd
 
      Downloads a video to mysamplevideo.mp4 from the specified Uri and saves it as 'mysamplevideo.mp4' in the $pwd directory.
 
    .EXAMPLE
      $link = (iwr -Method Get -Uri https://catalog.data.gov/dataset/national-student-loan-data-system-722b0 -SkipHttpErrorCheck -verbose:$false).Links.Where({ $_.href.EndsWith(".xls") })[0].href
      Start-DownloadWithRetry -Uri $link -Retries 3 -SecondsBetweenAttempts 10
 
      Attempts to download the file with a maximum of 3 retries, waiting 10 seconds between each attempt.
 
    .EXAMPLE
      Start-DownloadWithRetry -Uri $link -WhatIf
 
      Displays what would happen if the cmdlet runs without actually downloading the file.
 
    .EXAMPLE
      Start-DownloadWithRetry -Uri $link -Verbose
 
      Provides detailed output about the download process, including retry attempts and success/failure messages.
 
    .NOTES
      Author: Alain Herve
      Version: 1.1
 
    .LINK
      Online Version: https://github.com/chadnpc/cliHelper.Core/blob/main/Public/Network/WebTools/Start-DownloadWithRetry.ps1
  #>

  [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
  [Alias('DownloadWithRetry')][OutputType([IO.FileInfo])]
  [CmdletBinding(ConfirmImpact = 'Medium', SupportsShouldProcess = $false)]
  param(
    # Specifies the Uri of the file to download. This parameter is mandatory.
    [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
    [ValidateScript({
        if (([cryptobase]::IsValidUrl($_))) {
          return $true
        }; throw [System.ArgumentException]::new("Please Provide a valid Uri: $_", "Uri")
      })]
    [ValidateNotNullOrWhiteSpace()]
    [string]$Uri,

    # Specifies the name of the file to save locally. If not provided, the file name will be derived from the Uri.
    [Parameter(Mandatory = $false, Position = 1)]
    [Alias('n')][ValidateNotNullOrWhiteSpace()]
    [string]$Name,

    # Specifies the local directory where the file will be saved. Defaults to the current directory.
    [Parameter(Mandatory = $false, Position = 2)]
    [Alias('dlPath')][ValidateNotNullOrWhiteSpace()]
    [string]$DownloadPath = (Get-Location).Path,

    # Specifies the maximum number of retry attempts if the download fails. Defaults to 5.
    [Parameter(Mandatory = $false, Position = 3)]
    [Alias('r')]
    [int]$Retries = 5,

    # Specifies the delay, in seconds, between retry attempts. Defaults to 5 seconds.
    [Parameter(Mandatory = $false, Position = 4)]
    [Alias('s', 'timeout')]
    [int]$SecondsBetweenAttempts = 1,

    # Specifies a custom message to display during the download process.
    [Parameter(Mandatory = $false, Position = 5)]
    [Alias('m')]
    [string]$Message = "Downloading file",

    # Allows cancellation of the download operation using a System.Threading.CancellationToken.
    [Parameter(Mandatory = $false, Position = 6)]
    [System.Threading.CancellationToken]$CancellationToken = [System.Threading.CancellationToken]::None,

    [Parameter(Mandatory = $false, Position = 7)]
    [string]$caller
  )

  process {
    if ([String]::IsNullOrEmpty($Name)) { $Name = [IO.Path]::GetFileName($Uri) }
    $OutputFilePath = [IO.Path]::Combine([PsModuleBase]::GetUnResolvedPath($DownloadPath), $Name)
    try {
      $use_verbose = $VerbosePreference -eq 'Continue' -or $verbose.IsPresent
      $SplatParams = @{
        ScriptBlock            = { param([uri]$Uri, [string]$OutFile, $dlEvent, [bool]$verbose) $dlh = [DownloadHelper]::New(); return [IO.FileInfo]$dlh.DownloadFileAsync($Uri, $OutFile, $dlEvent, $verbose) }
        ArgumentList           = @([uri]$Uri, [string]$OutputFilePath, [DownloadHelper]::New(), [bool]$use_verbose)
        MaxAttempts            = $Retries
        SecondsBetweenAttempts = $SecondsBetweenAttempts
        Message                = $Message
        CancellationToken      = $CancellationToken
        Verbose                = $use_verbose
        Caller                 = $caller
      }
      $results = Invoke-RetriableCommand @SplatParams
    } catch {
      throw $_
    }
  }

  end {
    return $results.Output
  }
}