저는 $profile 에 정의해 두고 있습니다.
notepad $profile
Invoke-WebRequest
예제에 나오는 코드는 다음과 같습니다.
1
2
3
4
5
6
| $url = "http://mirror.internode.on.net/pub/test/10meg.test"
$output = "$PSScriptRoot\10meg.test"
$start_time = Get-Date
Invoke-WebRequest -Uri $url -OutFile $output
Write-Output "Time taken: $((Get-Date).Subtract($start_time).TotalSeconds) second(s)"
|
장점
- 가장 알기쉽고 간단한 형태입니다.
- 전체 사이즈를 알고 있다면, Write-Progress와 적절하게 연동하여 사용할 수 있습니다.
-Session
과 -WebSession
파라메터를 사용하면 Cookie도 사용할 수 있습니다.
단점
- 느립니다.
- HTTP response stream은 메모리에 버퍼링하고, 다운로드가 끝나면 파일에 flush 합니다. flush 하기 전까지 메모리를 점유하는 문제가 있습니다.
- Internet Explorer를 사용하기 때문에, Server Core 환경에서는 동작하지 않습니다.
판정
- HTTP Forms Auth를 사용하는 경우등 Cookie를 재사용하는 경우에는 적합한 선택입니다.
- 사이즈가 작은 파일에는 괜찮은 스피드입니다만, 스피드가 중요한 경우에는 다른 옵션을 선택하는 것이 좋습니다.
- $ProgressPreference = “SilentlyContinue”; 로 하고 실행하면 퍼포먼스가 좋아집니다. 빨라지는 정도는 네트웍에 따라 다릅니다.
함수로 만들어 두는 코드는 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| function download-file {
[CmdletBinding()]
Param (
[Parameter(ValueFromPipeline=$true)]
[string] $url,
[string] $path = $(pwd)
)
if (! (test-path $path)) { throw "no path found: $path"}
$filename = Split-Path $url -Leaf
$output = Join-Path $path $filename
$start_time = Get-Date
Invoke-WebRequest -Uri $url -OutFile $output
Write-Output "Time taken: $((Get-Date).Subtract($start_time).TotalSeconds) second(s)"
return $output
}
"http://ipv4.download.thinkbroadband.com/10MB.zip" | download-file
|
결과
1
2
3
4
5
6
7
8
9
10
11
12
13
| PS C:\Users\Administrator> "http://ipv4.download.thinkbroadband.com/10MB.zip" | download-file
Time taken: 3.3271723 second(s)
C:\Users\Administrator\10MB.zip
PS C:\Users\Administrator> dir .\10MB.zip
ディレクトリ: C:\Users\Administrator
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2020/09/14 22:24 10485760 10MB.zip
|
System.Net.WebClient
System.Net.WebClient
예제에 나오는 코드는 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
| $url = "http://mirror.internode.on.net/pub/test/10meg.test"
$output = "$PSScriptRoot\10meg.test"
$start_time = Get-Date
$wc = New-Object System.Net.WebClient
$wc.DownloadFile($url, $output)
#OR
(New-Object System.Net.WebClient).DownloadFile($url, $output)
Write-Output "Time taken: $((Get-Date).Subtract($start_time).TotalSeconds) second(s)"
|
장점
- Invoke-RestMethod 만큼 사용하기 쉽고, One Line 으로 실행할 수 있습니다.
- HTTP response stream은 버퍼링하기 때문에 속도도 적절합니다.
- 동시에 여러파일을 다운로드 하고 싶다면 System.Net.WebClient.DownloadFileAsync 을 사용하는 것도 가능합니다.
- System.Net.Webclient 은 ftp로부터 다운로드 할 수도 있습니다.
단점
- progress indicator가 없습니다.
- 기본적으로 blocking코드이기 때문에, Thread가 종료하기까지 아무것도 할 수없습니다.
판정
- 속도가 빠릅니다. 속도가 빠른 것이 善입니다. # 면접에서는 이렇게 얘기하면 안 될 때도 있습니다.
- Core에서도 완벽하게 동작합니다.
기타
- 이벤트를 등록해서 progress를 사용하는 방법도 있긴 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| # global variables
$global:lastpercentage = -1
$global:are = New-Object System.Threading.AutoResetEvent $false
# variables
$uri = "http://mirror.internode.on.net/pub/test/10meg.test"
$of = "10meg.test"
# web client
# (!) output is buffered to disk -> great speed
$wc = New-Object System.Net.WebClient
Register-ObjectEvent -InputObject $wc -EventName DownloadProgressChanged -Action {
# (!) getting event args
$percentage = $event.sourceEventArgs.ProgressPercentage
if($global:lastpercentage -lt $percentage)
{
$global:lastpercentage = $percentage
# stackoverflow.com/questions/3896258
Write-Host -NoNewline "`r$percentage%"
}
} > $null
Register-ObjectEvent -InputObject $wc -EventName DownloadFileCompleted -Action {
$global:are.Set()
Write-Host
} > $null
$wc.DownloadFileAsync($uri, $of);
# ps script runs probably in one thread only (event is reised in same thread - blocking problems)
# $global:are.WaitOne() not work
while(!$global:are.WaitOne(500)) {}
|
함수로 만들어 두는 코드는 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| function download-file {
[CmdletBinding()]
Param (
[Parameter(ValueFromPipeline=$true)]
[string] $url,
[string] $path = $(pwd)
)
if (! (test-path $path)) { throw "no path found: $path"}
$filename = Split-Path $url -Leaf
$output = Join-Path $path $filename
$start_time = Get-Date
(New-Object System.Net.WebClient).DownloadFile($url, $output)
Write-Output "Time taken: $((Get-Date).Subtract($start_time).TotalSeconds) second(s)"
return $output
}
"http://ipv4.download.thinkbroadband.com/10MB.zip" | download-file
|
Start-BitsTransfer
예제에 나오는 코드는 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
| $url = "http://mirror.internode.on.net/pub/test/10meg.test"
$output = "$PSScriptRoot\10meg.test"
$start_time = Get-Date
Import-Module BitsTransfer
Start-BitsTransfer -Source $url -Destination $output
#OR
Start-BitsTransfer -Source $url -Destination $output -Asynchronous
Write-Output "Time taken: $((Get-Date).Subtract($start_time).TotalSeconds) second(s)"
|
장점
- 가장 빠릅니다.
- progress를 사용할 수 있습니다.
- 다운로드를 실패할 경우 retry를 사용할 수 있습니다.
단점
- 대게 디폴트로 On입니다만, 언제나 On이라고 보장할 수는 없습니다.
- 또 백그라운드로 동작하도록 설계되어 있기 때문에, Queue에 대기열에 걸려 바로 다운로드 되지 않을 수 있습니다.
- ftp에서 파일을 다운로드 할 수는 없습니다.
판정
- 다운로드 시간이 중요하지 않을 경우, 또는 bandwidth를 지정하고 싶은 경우에는 베스트 솔루션입니다.
- BITS는 모니터링하기도 감시하기도 쉽습니다.
-Priority Foreground
옵션을 사용하면 최상위 우선순위로 실행할 수 있습니다.
함수로 만들어 두는 코드는 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| function download-file {
[CmdletBinding()]
Param (
[Parameter(ValueFromPipeline=$true)]
[string] $url,
[string] $path = $(pwd)
)
if (! (test-path $path)) { throw "no path found: $path"}
$filename = Split-Path $url -Leaf
$output = Join-Path $path $filename
$start_time = Get-Date
Import-Module BitsTransfer
Start-BitsTransfer -Source $url -Destination $output
Write-Output "Time taken: $((Get-Date).Subtract($start_time).TotalSeconds) second(s)"
return $output
}
"http://strawberryperl.com/download/5.30.2.1/strawberry-perl-5.30.2.1-64bit.msi" | download-file
|
결과
1
2
3
4
5
6
7
8
9
10
11
12
13
| PS C:\Users\Administrator> "http://strawberryperl.com/download/5.30.2.1/strawberry-perl-5.30.2.1-64bit.msi" | download-file
Time taken: 3.754872 second(s)
C:\Users\Administrator\strawberry-perl-5.30.2.1-64bit.msi
PS C:\Users\Administrator> dir .\strawberry-perl-5.30.2.1-64bit.msi
ディレクトリ: C:\Users\Administrator
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 2020/03/17 13:40 106541628 strawberry-perl-5.30.2.1-64bit.msi
|
이상으로 인터넷에서 파일을 다운로드 하는 함수를 작성해 보았습니다. 그대로 사용하셔도 문제없습니다만, crawler와 함께, async하게 사용할 때, 간단하게 응용하여 사용하면 하면 더욱 빛을 발합니다.
레퍼런스