PowerShell Wget:轻松下载文件

你是否需要从网络上下载文件,但讨厌不断点击链接?如果你的工作经常需要从网络上下载文件,你可能希望自动化这个任务。为什么不使用 PowerShell 来下载文件,就像使用 PowerShell wget 的替代方法一样呢?

Windows PowerShell 和 PowerShell 都具备文件下载功能。使用 PowerShell 下载文件只需要知道要使用哪些 cmdlet 和 .NET 类以及如何使用它们。

在本文中,你将学习使用 PowerShell 从网络上下载文件的各种方法。

先决条件

由于这是一篇“边做边学”的文章,有一些先决条件,以确保你能够按照示例进行操作。以下是基本要求。

  • A computer that is running on Windows 10 or higher. This computer is where you will run the scripts/commands featured in this article.
  • Windows PowerShell 5.1PowerShell 7.1(推荐)
    • Windows 10 已经包含了 Windows PowerShell 5.1。
  • A web site that hosts the files to download.
    • 对于非身份验证的文件下载,可以考虑使用免费的 Tele2 Speedtest 网站。
    • 如果你想进行带有授权的文件下载测试,可能需要构建自己的 HTTP 文件服务器。一个免费的 HTTP 文件服务器的示例是 HFS by Rejetto

使用 PowerShell 从 URL 下载文件的四种方法

有四种方法可以使用 PowerShell 下载文件,这些方法不依赖于第三方工具。它们分别是:

  • Invoke-WebRequest
  • Invoke-RestMethod
  • Start-BitsTransfer
  • .NET WebClient类

无论您使用这四种方法中的哪一种,使它们工作的逻辑和组件都是相同的。必须有一个指向文件位置的源URL和保存下载文件的目标路径。如果Web服务器要求,您还需要输入凭据。

接下来的章节将展示这四种方法的每一种。最后,您可以自行决定在使用PowerShell下载文件时采用哪种方式。

使用Invoke-WebRequest作为PowerShell的wget替代方法

在PowerShell中下载文件的第一种方法是使用Invoke-WebRequest cmdlet。也许是本文中最常用的cmdlet,Invoke-WebRequest可以下载HTTP、HTTPS和FTP链接。

无论源位置是否需要用户登录,Invoke-WebRequest cmdlet都可以处理带有凭据的请求。

为了下载一个文件,下面的语法显示了实现所需结果的最小参数。

Invoke-WebRequest -Uri <source> -OutFile <destination>

例如,下面的代码从一个网站下载名为10MB.zip的文件。然后将下载的文件保存到C:\dload\10MB.zip。您可以将下面的代码复制并粘贴到您的PowerShell会话中进行测试。

#源文件位置
$source = 'http://speedtest.tele2.net/10MB.zip'
#保存文件的目标位置
$destination = 'c:\dload\10MB.zip'
#下载文件
Invoke-WebRequest -Uri $source -OutFile $destination

下面的演示展示了在PowerShell中运行上述代码后的预期结果。如你所见,文件下载成功。

PowerShell wget : Downloading a file using Invoke-WebRequest

如果源文件要求进行身份验证才能访问怎么办?例如,下面的代码从一个需要用户登录的私有网站下载文件。

$source = 'https://mirror.lzex.ml/100MB.zip'
$destination = 'c:\dload\100MB.zip'
Invoke-WebRequest -Uri $source -OutFile $destination

然而,由于未经授权访问,下载失败了。

Downloading failed due to unauthorized access

如果需要进行身份验证,你应该使用-Credential参数为请求添加凭据。下面代码中的第一行会提示你输入凭据(用户名和密码),并将其存储到$credential变量中。

$credential = Get-Credential
$source = 'https://mirror.lzex.ml/100MB.zip'
$destination = 'c:\dload\100MB.zip'
Invoke-WebRequest -Uri $source -OutFile $destination -Credential $credential

下面的演示展示了当你在PowerShell中运行上述代码时会看到的结果。如你所见,Get-Credential命令提示了一个PowerShell凭据请求。这次,使用凭据和Invoke-WebRequest成功进行了下载。

Downloading a file with authentication

相关: 使用PowerShell的Get-Credential命令和所有与凭据相关的事项

在使用Invoke-WebRequest时,要注意解析错误

A crucial thing to remember when using Invoke-WebRequest in Windows PowerShell is that, by default, this cmdlet uses the Internet Explorer engine to parse data. The error below may happen when using Invoke-WebRequest on computers without the Internet Explorer in it.

你需要重新发送命令,但这次要包括-UseBasicParsing开关。

Invoke-WebRequest -Uri <source> -OutFile <destination> -UseBasicParsing

在Windows PowerShell中,可能会收到错误消息:无法解析响应内容,因为Internet Explorer引擎不可用,或者Internet Explorer的首次启动配置未完成。请指定UseBasicParsing参数并重试。

从PowerShell Core 6.0开始,Invoke-WebRequest cmdlet只使用基本解析。因此,-UseBasicParsing参数不再必要。

使用Invoke-RestMethod

Invoke-RestMethod cmdlet更适合向RESTful web服务发送HTTP或HTTPS请求。此cmdlet更适用于与REST API(如Microsoft Graph API)交互的请求。

当涉及直接从Web下载文件时,Invoke-RestMethod是一个很好的选择。不要被误导以为有什么不同。在从直接的Web链接下载文件时,使用Invoke-RestMethod和Invoke-WebRequest没有太大区别。

使用Invoke-RestMethod下载文件

要使用Invoke-RestMethod下载文件,请使用下面的语法。您会注意到,该命令使用与Invoke-WebRequest相同的参数。

Invoke-RestMethod -Uri <source> -OutFile <destination>

在下面的示例代码中,文件从$source变量中的URL值下载,然后保存到$destination变量中定义的路径中。

$source = 'http://speedtest.tele2.net/10MB.zip'
$destination = 'c:\dload\10MB.zip'
Invoke-RestMethod -Uri $source -OutFile $destination

如果源需要身份验证,您可以使用 -Credential 参数传递凭据。下面的示例会提示您输入凭据,并将其存储到变量 $credential 中,然后将 $credential 变量的值传递给 -Credential 参数。

另外,由于文件链接是一个HTTP源而不是HTTPS,这意味着您正在发送未加密的身份验证信息。通常情况下,为了安全起见,应避免使用HTTP源。但如果您必须使用HTTP源,您需要在命令中添加 -AllowUnencryptedAuthentication 开关。

$credential = Get-Credential
$source = 'http://speedtest.tele2.net/10MB.zip'
$destination = 'c:\dload\10MB.zip'
Invoke-RestMethod -Uri $source -OutFile $destination -Credential $credential -AllowUnencryptedAuthentication

使用 Start-BitsTransfer

Start-BitsTransfer 是专门用于在客户端和服务器计算机之间传输文件的。这个PowerShell cmdlet 依赖于 Windows 操作系统本身的 Background Intelligent Transfer Service(BITS)

由于 Start-BitsTransfer 需要 BITS 才能工作,这意味着这个 cmdlet 在非 Windows 计算机上不可用。但另一方面,Start-BitsTransfer 本身享受了 BITS 的好处。其中一些好处包括:

  • 网络带宽和使用情况的感知。
  • 中断处理(续传、自动续传、暂停等)
  • 作为后台作业下载多个文件。
  • 能够设置下载作业的优先级。

下载文件

在PowerShell中使用Start-BitsTransfer下载文件的基本方法是指定源和目标。使用下面的脚本,您只需要根据您的要求更改$source$destination的值。

$source = 'http://speedtest.tele2.net/100MB.zip'
$destination = 'c:\dload\100MB.zip'
Start-BitsTransfer -Source $source -Destination $destination

从下面的演示中可以看到,文件被下载到路径c:\dload\100MB.zip

Downloading a file using Start-BitsTransfer

假设未指定目标,则Start-BitsTransfer将文件下载并保存到当前工作目录。例如,如果您从C:\dload运行Start-BitsTransfer,该文件将下载到同一目录。

对于需要身份验证的下载,Start-BitsTransfer有一个-Credential参数,接受一个PSCredential对象。

下载多个文件

为了演示下载多个文件,您需要创建一个包含两列的CSV文件。将文件命名为filelist.txt。第一列应包含源链接,而第二列必须包含目标路径。文件内容如下所示。

# source,destination
http://speedtest.tele2.net/1MB.zip,c:\dload\1MB.zip
http://speedtest.tele2.net/10MB.zip,c:\dload\10MB.zip
http://speedtest.tele2.net/100MB.zip,c:\dload\100MB.zip

相关:使用Import-Csv在PowerShell中管理CSV文件

一旦CSV文件准备好,使用下面的命令开始文件下载。该命令使用Import-Csv导入CSV文件,并将内容传递给Start-BitsTransfer

Import-Csv .\filelist.csv | Start-BitsTransfer

请参考下面的演示,了解上面的代码如何工作。如您所见,下载开始,并且您可以看到下载进度。在下载过程中,PowerShell提示符不可用。

Starting a synchronous multiple file download

假设您想要将下载过程作为后台作业启动。要这样做,您只需在Start-BitsTransfer命令的末尾添加-Asynchronous开关。

Import-Csv .\filelist.csv | Start-BitsTransfer -Asynchronous

最初,每个作业的状态将显示为“connecting”(连接中)。下面的截图显示了每个文件下载的作业ID。

Starting file download as background jobs

现在您已经开始了下载过程,您将想要检查下载是否已完成。要检查下载作业的状态,请使用Get-BitsTransfer cmdlet。如下所示,下载作业的状态已更改为“Transferred”(已传输)。

Viewing the file download job status

使用WebClient类和HttpClient类(.NET框架)

PowerShell基于.NET,并且其本质使其能够利用.NET本身的功能。在PowerShell中,有两个.NET类可用于下载文件:WebClientHttpClient

如果您想以更多的开发和技术方式了解这两个.NET类,您可以从以下开始→ 何时使用WebClient vs. HttpClient vs. HttpWebRequest在下一节中,您将学习如何在PowerShell中使用WebClient和HttpClient从网络下载文件。

使用System.Net.WebClient下载文件

要使用WebClient类,您需要将一个对象初始化为System.Net.WebClient类型。在下面的示例中,$webClient是新的System.Net.WebClient对象。然后,使用DownloadFile()方法开始从源下载文件。

相关: 使用PowerShell数据类型加速器加快编码速度

请复制下面的代码并在您的PowerShell会话中运行以进行测试。请注意,除非出现错误,否则您将在屏幕上看不到任何进度或输出。但是,PowerShell提示符将被锁定,直到下载完成。

#定义源链接和目标路径
$source = 'http://speedtest.tele2.net/10MB.zip'
$destination = 'c:\dload\10MB.zip'
#创建新的WebClient
$webClient = [System.Net.WebClient]::new()
#下载文件
$webClient.DownloadFile($source, $destination)

如果源文件需要身份验证才能下载,您可以使用下面的代码。第一行提示输入凭据,并将其存储在变量$credentials中。然后,在文件下载请求中包含$credential的值。

# 提示输入用户名和密码
$credentials = Get-Credential
$source = 'http://speedtest.tele2.net/10MB.zip'
$destination = 'c:\dload\10MB.zip'
# 创建新的WebClient
$webClient = [System.Net.WebClient]::new()
# 添加凭据
$webClient.Credentials = $credentials
# 下载文件
$webClient.DownloadFile($source, $destination)

根据这个Microsoft文档:“我们不建议您在新的开发中使用WebClient类。相反,使用System.Net.Http.HttpClient类。”

看起来WebClient类已经过时了,Microsoft推荐的新类是HttpClient类。不过,不用担心。下一节将介绍如何在PowerShell中使用HttpClient类下载网络文件。

使用System.Net.Http.HttpClient下载文件

与WebClient类一样,您需要首先创建System.Net.Http.HttpClient。使用下面的代码从$source下载文件到$destination。请参考每行代码上面的注释,了解每行代码的作用。

以下代码是实时的,您可以在 PowerShell 会话中运行它进行测试。

# 设置源和目标
$source = 'http://speedtest.tele2.net/10MB.zip'
$destination = 'c:\dload\10MB.zip'
 
# 创建 HTTP 客户端下载请求
$httpClient = New-Object System.Net.Http.HttpClient
$response = $httpClient.GetAsync($source)
$response.Wait()
 
# 创建指向输出文件目标的文件流
$outputFileStream = [System.IO.FileStream]::new($destination, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write)
 
# 将下载流式传输到目标文件流
$downloadTask = $response.Result.Content.CopyToAsync($outputFileStream)
$downloadTask.Wait()
 
# 关闭文件流
$outputFileStream.Close()

在需要身份验证的文件下载情况下,您需要将凭据添加到 HttpClient 对象中。要将凭据包含到文件下载请求中,请创建一个新的 System.Net.Http.HttpClientHandler 对象以存储凭据。

您可以复制以下代码并在 PowerShell 中运行进行测试。或者您也可以将其作为 PowerShell 脚本运行。在此示例中,代码保存为 download-file.ps1

# 设置源和目标
$source = 'http://speedtest.tele2.net/10MB.zip'
$destination = 'c:\dload\10MB.zip'
 
# 提示输入凭据
$credentials = Get-Credential

# 创建带凭据的 HTTP 客户端下载请求
$handler = New-Object System.Net.Http.HttpClientHandler
$handler.Credentials = $credentials
$httpClient = New-Object System.Net.Http.HttpClient($handler)
$response = $httpClient.GetAsync($source)
$response.Wait()
 
# 创建指向输出文件目标的文件流
$outputFileStream = [System.IO.FileStream]::new($destination, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write)
 
# 将下载流式传输到目标文件流
$downloadTask = $response.Result.Content.CopyToAsync($outputFileStream)
$downloadTask.Wait()
 
# 关闭文件流
$outputFileStream.Close()

下面的演示展示了运行PowerShell脚本下载文件的结果。

开始时,目录中只有脚本文件。会提示输入用户名和密码。然后,脚本开始下载文件。下载完成后,你可以看到新文件已经在目标目录中。

Downloading a file using the .NET HttpClient class

结论

Windows PowerShell和PowerShell Core都具备内置的文件下载功能,可作为PowerShell wget的替代品!无论是下载受密码保护的资源、单个文件还是多个文件,PowerShell都为你提供了解决方案。

本文介绍的文件下载方法适用于Windows PowerShell和PowerShell Core。这意味着这些方法适用于Windows和非Windows系统,但不包括Start-BitsTransfer

由于PowerShell不仅仅是一个命令提示符,你可以将所学内容转化为脚本。这对你来说意味着自动化的机会。不再需要手动复制URL、点击链接和等待下载。

Source:
https://adamtheautomator.com/powershell-download-file/