FTPでディレクトリごとダウンロード by powershell

<h3>環境</h3>
Windows10

<h3>概要/背景</h3>
仕事で定期的にFTPサーバから特定のディレクトリのファイルをダウンロードする必要があったので、自動化しました。
powershellでファイル単体で落とすのは探すと色々あるのですが、ディレクトリごと落としてくる方法が見つからないので作りました。基本的にはディレクトリを指定して、中のファイルごとに落とすだけ。ただTIME_WAIT問題でセッションが残ってしまうため、接続制限エラーに出くわします。すでにダウンロードしたファイルは落とさないようにしてセッションの無駄遣いを避けています。
自分の場合は週次更新でファイル数自体は少ないので、これでも問題なしでした。定期的に、ディレクトリを指定して、そんなに多くないファイルを落としたい場合に使えると思います。
<h3>機能</h3>
ディレクトリ名を指定して、その中にあるファイルの内、ローカルに存在しなものをFTPサーバからダウンロードします。バッチファイル等でディレクトリを指定して、Windowsのスタートアップとかに登録しておけば1日1回のログインのときに動いて便利。

<h3>ダウンロードスクリプト（ftp_recursive_r1.ps1）</h3>
```powershell
function DownloadFtpDirectory($url, $credentials, $localPath)
{
    $listRequest = [Net.WebRequest]::Create($url)
    $listRequest.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectoryDetails
    $listRequest.Credentials = $credentials
    $lines = New-Object System.Collections.ArrayList
    $listResponse = $listRequest.GetResponse()
    $listStream = $listResponse.GetResponseStream()
    $listReader = New-Object System.IO.StreamReader($listStream)
    while (!$listReader.EndOfStream)
    {
        $line = $listReader.ReadLine()
        $lines.Add($line) | Out-Null
    }
    $listReader.Dispose()
    $listStream.Dispose()
    $listResponse.Dispose()
    foreach ($line in $lines)
    {
        $tokens = $line.Split(" ", 9, [StringSplitOptions]::RemoveEmptyEntries)
        $name = $tokens[8]
        $permissions = $tokens[0]
        $localFilePath = Join-Path $localPath $name
        $fileUrl = ($url + $name)
        if ($permissions[0] -eq 'd')
        {
            if (!(Test-Path $localFilePath -PathType container))
            {
                Write-Host "Creating directory $localFilePath"
                New-Item $localFilePath -Type directory | Out-Null
            }
            DownloadFtpDirectory ($fileUrl + "/") $credentials $localFilePath
        }
        else
        {
			if(!(Test-Path $localFilePath)){
				Write-Host "Downloading $fileUrl to $localFilePath"
				$LocalFileFile = New-Object IO.FileStream ($localFilePath,[IO.FileMode]::Create)
				$downloadRequest = [System.Net.FtpWebRequest]::Create($fileUrl)
				$downloadRequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
				$downloadRequest.Credentials = $credentials
				$downloadRequest.UseBinary = $true
				$downloadRequest.KeepAlive= $false
				$downloadResponse = $downloadRequest.GetResponse()
				$sourceStream = $downloadResponse.GetResponseStream()
				[byte[]]$ReadBuffer = New-Object byte[] 1024
				do {
					$ReadLength = $sourceStream.Read($ReadBuffer,0,1024)
					$LocalFileFile.Write($ReadBuffer,0,$ReadLength)
				}
				while ($ReadLength -ne 0)
				$sourceStream.Dispose()
				$downloadResponse.Dispose()
			}else{
				Write-Host "File already exists"
			}
        }
    }
}

$credentials = New-Object System.Net.NetworkCredential('FTPサーバログインid', 'FTPサーバログインpass') 
$url = 'ftp://FTPサーバアドレス!'+ $Args[0] + "/"
$mypath=Split-Path ( & { $myInvocation.ScriptName } ) -parent
$dirDest = $mypath+"\" + $Args[0] + "/"

DownloadFtpDirectory $url $credentials $dirDest

```

<h3>呼び出し用バッチファイル</h3>
```bat
@echo off
echo Now Scripting
for %%j in (directory_No1 directory_No2 directory_No3) do (powershell -NoProfile -ExecutionPolicy Unrestricted ./ftp_recursive_r1.ps1 %%j)
rem powershell -NoProfile -ExecutionPolicy Unrestricted ./ftp_sample2.ps1 %%i

echo Finished! Press Enter.
pause > nul
exit
```
<h3>参考</h3>
https://qiita.com/kuni-nakaji/items/c07004c7d9e5bb683bc2
https://qiita.com/sengoku/items/d214e7a8d1e399b42b18
https://qiita.com/arachan@github/items/896921ac232c1de97021