Functions/Get-FileName.ps1

[CmdletBinding()]
param (
  [Parameter(Mandatory)]
  [uri]
  $Uri,

  [ValidateNotNullOrEmpty()]
  [string]
  $UserAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36',

  [string]
  $Referer
)

Set-HttpClient -Uri $Uri

Write-Host ('{0}からファイル名を取得します。' -f $Uri.AbsoluteUri) -ForegroundColor Cyan

$global:HttpClient.DefaultRequestHeaders.Add('User-Agent', $UserAgent)
if ($Referer) {
  $global:HttpClient.DefaultRequestHeaders.Add('Referer', $Referer)
}

$httpHeader = $global:HttpClient.GetAsync($Uri, 1)

if ($httpHeader.Result.StatusCode -ne 'OK') {
  throw 'ファイル名取得の為にリクエストを送りましたが、正常に完了しませんでした。'
}

$contentType = $httpHeader.Result.Content.Headers.ContentType.MediaType

if ($contentType -eq 'text/html') {
  throw 'HTTPヘッダのContent-Typeがtext/htmlでした。'
}

# ContentDispositionから取得
$ContentDispositionFileName = [regex]::Replace($httpHeader.Result.Content.Headers.ContentDisposition.FileName, '"', '')

if ($ContentDispositionFileName) {
  return $ContentDispositionFileName
}

# リダイレクト後のリクエストURIの葉から取得
$RequestUri = $httpHeader.Result.RequestMessage.RequestUri
$RequestUriLeaf = $RequestUri.LocalPath | Split-Path -Leaf

if (!$RequestUriLeaf -match '.+\..+') {
  Write-Warning '取得したファイル名に拡張子がありませんでした。取得に失敗した可能性があります。'
}
return $RequestUriLeaf

<#
  .SYNOPSIS
  ダウンロード時のファイル名を取得します。
  .DESCRIPTION
  ダウンロード時のファイル名を取得します。
  レスポンスヘッダのContent-Typeがtext/htmlとなるものに対しては例外を投げます。
  .PARAMETER Uri
  ファイルのダウンロードリンクを指定します。
  .PARAMETER UserAgent
  HTTPリクエストの為のユーザエージェントを指定します。
  .PARAMETER Referer
  HTTPリクエストの為のリファラを指定します。
  .INPUTS
  なし
  .OUTPUTS
  System.String
    ダウンロードする際のファイル名を返します。
  .EXAMPLE
  PS C:\>Get-FileName -Uri 'https://go.microsoft.com/fwlink/?LinkID=799445'
 
  この例では、Windows Update Assistantのインストーラのファイル名を取得します。
  .LINK
  .NOTES
  curlでの実装を考えたが、httpヘッダを取得した時点でリクエストを完了とする方法が分からなかった。
  -IオプションでのHEADリクエストは拒否するサーバーがあるので不可。
  curlでHTTPヘッダを取得する場合は以下のようにします。
  $httpHeader = curl -sL -D - -o /dev/null $Uri
#>