對Linux熟悉的讀者可能會對Linux通過wget下載文件有印象,這個工具功能很強(qiáng)大,在.NET環(huán)境下提到下載文件大多數(shù)人熟悉的是通過System.Net.WebClient進(jìn)行下載,這個程序集能實(shí)現(xiàn)下載的功能,但是有缺陷,如果碰上類似于…/scripts/?dl=417這類的下載鏈接將無法正確識別文件名,下載的文件通常會被命名為dl=417這樣古怪的名字,其實(shí)對應(yīng)的文件名是在訪問這個鏈接返回結(jié)果的HTTP頭中包含的。事實(shí)上微軟也提供了避免這些缺陷的程序集System.Net.HttpWebRequest 和 HttpWebResponse,本文將會使用這兩個程序集來實(shí)現(xiàn)PowerShell版wget的功能。
代碼不怎么復(fù)雜,基本上就是創(chuàng)建HttpWebRequest對象,設(shè)定UserAgent和CookieContainer以免在遇到設(shè)置防盜鏈的服務(wù)器出現(xiàn)無法下載的情況。然后通過HttpWebRequest對象的GetResponse()方法從http頭中獲取目標(biāo)文件的大小以及文件名,以便能在下載到文件時提示當(dāng)前下載進(jìn)度,在下載完文件后,列出當(dāng)前目錄下對應(yīng)的文件。代碼不復(fù)雜,有任何疑問的讀者可以留言給我,進(jìn)行交流,下面上代碼:
=====文件名: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 #39;$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]'#40;?i)filename=(.*)$').Match( $res.Headers["Content-Disposition"] ).Groups[1].Value
$fileName = $fileName.trim("#92;/""'")
$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
}
}
能夠看到在下載文件的過程中會顯示當(dāng)前已下載數(shù)和總的文件大小,并且有進(jìn)度條顯示當(dāng)前下載的進(jìn)度,跟wget看起來是有些神似了。下載完畢后會顯示已經(jīng)下載文件的情況。