PowerShell小技巧之实现文件下载(类wget)

对Linux熟悉的读者可能会对Linux通过wget下载文件有印象,这个工具功能很强大,在.NET环境下提到下载文件大多数人熟悉的是通过System.Net.WebClient进行下载,这个程序集能实现下载的功能,但是有缺陷,如果碰上类似于…/scripts/?dl=417这类的下载链接将无法正确识别文件名,下载的文件通常会被命名为dl=417这样古怪的名字,其实对应的文件名是在访问这个链接返回结果的HTTP头中包含的。事实上微软也提供了避免这些缺陷的程序集System.Net.HttpWebRequest 和 HttpWebResponse,本文将会使用这两个程序集来实现PowerShell版wget的功能。

代码不怎么复杂,基本上就是创建HttpWebRequest对象,设定UserAgent和CookieContainer以免在遇到设置防盗链的服务器出现无法下载的情况。然后通过HttpWebRequest对象的GetResponse()方法从http头中获取目标文件的大小以及文件名,以便能在下载到文件时提示当前下载进度,在下载完文件后,列出当前目录下对应的文件。代码不复杂,有任何疑问的读者可以留言给我,进行交流,下面上代码:

代码如下:

=====文件名:Get-WebFile.ps1=====
function Get-WebFile {
<# Author:fuhj(powershell#live.cn ,http://fuhaijun.com)
   Downloads a file or page from the web
.Example
  Get-WebFile http://mirrors.cnnic.cn/apache/couchdb/binary/win/1.4.0/setup-couchdb-1.4.0_R16B01.exe
  Downloads the latest version of this file to the current directory
#>

[CmdletBinding(DefaultParameterSetName="NoCredentials")]
   param(
      #  The URL of the file/page to download
      [Parameter(Mandatory=$true,Position=0)]
      [System.Uri][Alias("Url")]$Uri # = (Read-Host "The URL to download")
   ,
      #  A Path to save the downloaded content.
      [string]$FileName
   ,
      #  Leave the file unblocked instead of blocked
      [Switch]$Unblocked
   ,
      #  Rather than saving the downloaded content to a file, output it. 
      #  This is for text documents like web pages and rss feeds, and allows you to avoid temporarily caching the text in a file.
      [switch]$Passthru
   ,
      #  Supresses the Write-Progress during download
      [switch]$Quiet
   ,
      #  The name of a variable to store the session (cookies) in
      [String]$SessionVariableName
   ,
      #  Text to include at the front of the UserAgent string
      [string]$UserAgent = "PowerShellWget/$(1.0)"
   )

Write-Verbose "Downloading '$Uri'"
   $EAP,$ErrorActionPreference = $ErrorActionPreference, "Stop"
   $request = [System.Net.HttpWebRequest]::Create($Uri);
   $ErrorActionPreference = $EAP
   $request.UserAgent = $(
         "{0} (PowerShell {1}; .NET CLR {2}; {3}; http://fuhaijun.com)" -f $UserAgent,
         $(if($Host.Version){$Host.Version}else{"1.0"}),
         [Environment]::Version,
         [Environment]::OSVersion.ToString().Replace("Microsoft Windows ", "Win")
      )

$Cookies = New-Object System.Net.CookieContainer
   if($SessionVariableName) {
      $Cookies = Get-Variable $SessionVariableName -Scope 1
   }
   $request.CookieContainer = $Cookies
   if($SessionVariableName) {
      Set-Variable $SessionVariableName -Scope 1 -Value $Cookies
   }

try {
      $res = $request.GetResponse();
   } catch [System.Net.WebException] {
      Write-Error $_.Exception -Category ResourceUnavailable
      return
   } catch {
      Write-Error $_.Exception -Category NotImplemented
      return
   }

if((Test-Path variable:res) -and $res.StatusCode -eq 200) {
      if($fileName -and !(Split-Path $fileName)) {
         $fileName = Join-Path (Convert-Path (Get-Location -PSProvider "FileSystem")) $fileName
      }
      elseif((!$Passthru -and !$fileName) -or ($fileName -and (Test-Path -PathType "Container" $fileName)))
      {
         [string]$fileName = ([regex]'(?i)filename=(.*)$').Match( $res.Headers["Content-Disposition"] ).Groups[1].Value
         $fileName = $fileName.trim("\/""'")

$ofs = ""
         $fileName = [Regex]::Replace($fileName, "[$([Regex]::Escape(""$([System.IO.Path]::GetInvalidPathChars())$([IO.Path]::AltDirectorySeparatorChar)$([IO.Path]::DirectorySeparatorChar)""))]", "_")
         $ofs = " "

if(!$fileName) {
            $fileName = $res.ResponseUri.Segments[-1]
            $fileName = $fileName.trim("\/")
            if(!$fileName) {
               $fileName = Read-Host "Please provide a file name"
            }
            $fileName = $fileName.trim("\/")
            if(!([IO.FileInfo]$fileName).Extension) {
               $fileName = $fileName + "." + $res.ContentType.Split(";")[0].Split("/")[1]
            }
         }
         $fileName = Join-Path (Convert-Path (Get-Location -PSProvider "FileSystem")) $fileName
      }
      if($Passthru) {
         $encoding = [System.Text.Encoding]::GetEncoding( $res.CharacterSet )
         [string]$output = ""
      }

[int]$goal = $res.ContentLength
      $reader = $res.GetResponseStream()
      if($fileName) {
         try {
            $writer = new-object System.IO.FileStream $fileName, "Create"
         } catch {
            Write-Error $_.Exception -Category WriteError
            return
         }
      }
      [byte[]]$buffer = new-object byte[] 4096
      [int]$total = [int]$count = 0
      do
      {
         $count = $reader.Read($buffer, 0, $buffer.Length);
         if($fileName) {
            $writer.Write($buffer, 0, $count);
         }
         if($Passthru){
            $output += $encoding.GetString($buffer,0,$count)
         } elseif(!$quiet) {
            $total += $count
            if($goal -gt 0) {
               Write-Progress "Downloading $Uri" "Saving $total of $goal" -id 0 -percentComplete (($total/$goal)*100)
            } else {
               Write-Progress "Downloading $Uri" "Saving $total bytes..." -id 0
            }
         }
      } while ($count -gt 0)

$reader.Close()
      if($fileName) {
         $writer.Flush()
         $writer.Close()
      }
      if($Passthru){
         $output
      }
   }
   if(Test-Path variable:res) { $res.Close(); }
   if($fileName) {
      ls $fileName
   }
}

调用方法,如下:
Get-WebFile http://mirrors.cnnic.cn/apache/couchdb/binary/win/1.4.0/setup-couchdb-1.4.0_R16B01.exe
这里下载couchdb的最新windows安装包。
执行效果如下图所示:

能够看到在下载文件的过程中会显示当前已下载数和总的文件大小,并且有进度条显示当前下载的进度,跟wget看起来是有些神似了。下载完毕后会显示已经下载文件的情况。

(0)

相关推荐

  • PowerShell中使用Get-Alias命令获取cmdlet别名例子

    PowerShell中为了与原来的cmd命令保持兼容,特别为很多cmdlet设置了别名.这些别名跟cmd命令是同名的,但它代表着一个PowerShell中的cmdlet.比如dir这个cmd命令,它用于列出一个目录下的子文件夹和文件,在PowerShell有一个Get-ChildItem的cmdlet,它的作用也是列出一个目录下的子文件夹和文件,于是微软就给Get-ChildItem设置了一别名叫dir. 在PowerShell中,使用Get-Alias这个cmdlet,可以列出所有的别名,以及

  • PowerShell中使用Out-String命令把对象转换成字符串输出的例子

    本文介绍在PowerShell中如何将cmdlet输出的对象,转换为string字符串类型,便于后期的处理. PowerShell中的cmdlet输出的结果都是以对象(Object)的形式存在的.对于Object类型,在后期处理时有利有弊,当然利大于弊.但某些时候,我们希望cmdlet输出的结果直接是字符串,那应该怎么来实现呢? PowerShell中提供了一个Out-String的cmdlet,来看看它的介绍:Out-String cmdlet 将 Windows PowerShell 管理的

  • Powershell小技巧之使用Get-ChildItem得到指定扩展名文件

    当你使用Get-ChildItem获取输出文件列表时,你可能会发现即使用了-Filter参数也会比你预期的结果要多得多. 这里有一个例子,它不仅返回.ps1扩展名文件同时也返回.ps1xml扩展名文件: Get-ChildItem -Path C:\windows -Recurse -ErrorAction SilentlyContinue -Filter *.ps1 为了限制结果集只是你设置后的扩展名,可以添加一个命令的过滤器,以完善的结果: Get-ChildItem -Path C:\wi

  • PowerShell实现简单的grep功能

    在PowerShell中,无法像*nix中一样使用grep命令,直接对一个目录下的所有文件进行内容查找,下面的PS脚本针对目录和文件进行了区分,借用Select-String命令,实现了内容查找,并显示查找到的文件和匹配内容所在行号. 使用的时候,只需要在shell中,输入: "命令所在目录"\grep.ps1 "需要查找的字符串" "需要查找的路径" param($str, $path=".\") #输入参数,默认在当前目录

  • PowerShell中使用Get-ChildItem命令读取目录、文件列表使用例子和小技巧

    本文介绍一个PowerShell中使用Get-ChildItem这个cmdlet来获取目录下的文件列表.Get-ChildItem是获取子项目的意思,可以获取一个目录下的文件和子目录. 在DOS系统下,我们想查看一个目录下有哪些子目录和文件,我们可以通过dir命令来实现.在PowerShell中,dir命令貌似仍然可用.但洪哥告诉你,这个dir已经不是cmd.exe中的那个dir了,它是Get-ChildItem这个cmdlet的别名.也就是说,使用Get-ChildItem和使用dir得到的效

  • PowerShell中使用Get-Date获取日期时间并格式化输出的例子

    在PowerShell中有一个Get-Date的cmdlet,使用它可以直接返回当前的日期和时间.使用-Format参数可以返回当前的年.月.日.时.分.秒等. Get-Date的直接使用 在PowerShell中直接调用Get-Date,可以返回当前的日期和时间,包括年.月.日.时.分.秒.举例如下: 复制代码 代码如下: PS C:\Users\zhanghong> Get-Date 2013年9月9日 22:26:56 Write-Host中使用Get-Date 注意:在Write-Hos

  • PowerShell中使用Get-EventLog读取、筛选系统日志的例子

    本文介绍使用PowerShell读取系统日志,可以按日志名称.日志类型.时间等多个因素进行筛选.PowerShell中提供了一个叫Get-EventLog的cmdlet,使用它可以读取系统日志. 运行一下这个Get-EventLog的cmdlet试一下: 复制代码 代码如下: PS C:\Users\zhanghong> Get-EventLog -LogName System 噢!如果你感觉你被小编害了,cmd窗口一直在滚动个不停,那么按Ctrl+C先把它停下来.然后我解释一下,之所以发生这样

  • PowerShell小技巧之实现文件下载(类wget)

    对Linux熟悉的读者可能会对Linux通过wget下载文件有印象,这个工具功能很强大,在.NET环境下提到下载文件大多数人熟悉的是通过System.Net.WebClient进行下载,这个程序集能实现下载的功能,但是有缺陷,如果碰上类似于-/scripts/?dl=417这类的下载链接将无法正确识别文件名,下载的文件通常会被命名为dl=417这样古怪的名字,其实对应的文件名是在访问这个链接返回结果的HTTP头中包含的.事实上微软也提供了避免这些缺陷的程序集System.Net.HttpWebR

  • PowerShell小技巧之获取TCP响应(类Telnet)

    通常情况下,为了检测指定的TCP端口是否存活,我们都是通过telnet指定的端口看是否有响应来确定,然而默认情况下win8以后的系统默认是不安装telnet的.设想一下如果你黑进了一个服务器,上面没装telnet,但是为了进一步渗透进内网,需要探测内部服务器特定端口是否打开,同时你还不愿意安装telnet,担心引起管理员注意.那么好吧,在这个情况下你需要我的这个脚本.由于它是原生态的PowerShell语句完成,木有telnet你也照样能检测TCP端口的情况了. 下面首先上代码,后面进行讲解:

  • PowerShell小技巧之使用Hotmail账号发送邮件

    在低版本的PowerShell上发送邮件可以借助.NET的system.net.mail.smtpclient类.在高版本的PowerShell中可以借助现成的命令:Send-MailMessage 我在尝试使用Hotmail时,遇到了一个错误: Send-MailMessage : The SMTP server requires a secure connection or the client was not authenticated. The server response was:

  • Powershell小技巧之使用WMI工具

    WMI是一个强大的技术:只需要简单的指定一个WMI类名就能返回它类的所有实例: 复制代码 代码如下: PS> Get-WmiObject -Class Win32_BIOS SMBIOSBIOSVersion : 76CN27WW Manufacturer      : LENOVO Name              : 76CN27WW SerialNumber      : 1006250300406 Version           : LENOVO - 1 你如何知道它有哪些类呢?这

  • Powershell小技巧之获取MAC地址

    在Powershell中获取MAC地址不是很难.这里就有一种方法: 复制代码 代码如下: PS> getmac /FO CSV | ConvertFrom-Csv Physical Address                        Transport Name                         ----------------                        --------------                         5C-51-4F-6

  • Powershell小技巧之使用Jint引擎在PowerShell中执行Javascript函数

    这里演示如何利用PowerShell将一段Javascript函数字符串交给Jint引擎去执行. 执行Javascript函数 .Net版的Javascript解释器 可以从Git上获取Jint的代码,也可以从nuget上下载Jint的程序集. Jint是一个面向.Net的Javascript解释器.Jint不会把Javascript编译成.Net字节码,所以它非常适用于脚本小且运行起来速度快的工程,或者运行在不同平台上的脚本. PowerShell调用 拿到Dll根据-Path参数直接使用Ad

  • Powershell小技巧之使用WS-Man来调用PowerShell命令

    虽然PowerShell远程管理被构建在 WS-Management的之上,但它是协议中的协议.如果尝试使用 PSRP (PowerShell远程处理协议)直接进行交互,本质上需要在客户端机器上运行一个PowerShell副本.另一种方法是使用一个鲜为人知的远程命令行工具,称为WinRS.WinRS是一个简单的工具,允许远程CMD.EXE,它也是构建在WS-Management之上的.所不同的是WinRS重用了 WS-Transfer中的Create和Delete,并引入了一些新的自定义的SOA

  • Powershell小技巧之获取当前的时间并转换为时辰

    午时三刻已到,行刑,刀下留人,现在到底是不是午时,能否让PowerShell告诉我呢? 好的, 没问题.从晚上23点到凌晨2点之间属于子时,每两个小时一个时辰,依次为"子丑寅卯辰巳午未申酉戌亥". 函数获取当前时辰 用PowerShell脚本实现: function Get-ChinaTimeAlias { param( [ValidateRange(0,23)] [int]$Hour = (get-date).Hour ) $timeAliasArray='子丑寅卯辰巳午未申酉戌亥'

  • Powershell小技巧之使用WMI查询插上的U盘

    如果你想知道当前插在你电脑上的USB设备,WMI能帮助你: Get-WmiObject -Class Win32_PnPEntity | Where-Object { $_.DeviceID -like 'USBSTOR*' } 这将返回所有插上在使用的USBSTOR设备类 如果你使用WMI查询语言(WQL),你甚至可以使用筛选命令: Get-WmiObject -Query 'Select * From Win32_PnPEntity where DeviceID Like "USBSTOR%

  • Powershell小技巧之查询AD用户

    假若你登录到了一个AD中你可以轻松的查询AD目录.在先前的技巧中我们阐述了一个基本脚本.这里有一个可以自定义根目录的扩展方法(设置你的查询点),它支持同步查询(而不是递归到一个容器). 它同时也阐述了怎么从一个活动目录查找结果再将其转化成用户对象: $SAMAccountName = 'tobias' $SearchRoot = 'LDAP://OU=customer,DC=company,DC=com' $SearchScope = 'OneLevel' $ldap = "(&(obj

随机推荐