0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

PowerShellでFTPしたい

Last updated at Posted at 2023-06-24

この時代に FTP を使うことになったため、不本意ながら PowerShell でそれらしいものを作りました。
FtpWebRequest は公式にも新規開発には使うなと記載されている点には留意してください。
エラーハンドリングはしていませんので、外側で $ErrorActionPreference = "Stop" して try catch してください。
リソース管理はちょっと気にしたけど十分ではないかもしれない。

実装

function New-FtpRequest {
    param(
        [parameter(Mandatory=$True)]$ip, 
        [parameter(Mandatory=$True)]$user, 
        [parameter(Mandatory=$True)]$password, 
        [parameter(Mandatory=$True)]$Path
    )
    $req = [System.Net.FtpWebRequest]::Create("ftp://${ip}${Path}")
    $req.Credentials = New-Object System.Net.NetworkCredential($user, $password)
    return $req
}
function Invoke-FtpOpen {
    param(
        [parameter(Mandatory=$True)]$Connection
    )
    $req = New-FtpRequest @Connection -Path "/"
    $req.Method = [System.Net.WebRequestMethods+ftp]::PrintWorkingDirectory
    $req.keepalive = $True
    $res = $req.GetResponse()
    return $res.WelcomeMessage
}
function Invoke-FtpPut {
    param(
        [parameter(Mandatory=$True)]$Connection, 
        [parameter(Mandatory=$True)]$File,
        [parameter(Mandatory=$False)]$Path = "/"
    )
    $File = Get-Item $File
    if ($Path -match "/$") {
        $Path = $Path + $File.Name
    }
    $req = New-FtpRequest @Connection -Path $Path
    $req.Method = [System.Net.WebRequestMethods+ftp]::UploadFile
    $reqStream = $req.GetRequestStream()

    $fileStream = [system.io.file]::OpenRead($File.FullName)

    $fileStream.CopyTo($reqStream)

    $fileStream.Dispose()
    $reqStream.Dispose()

    $res = $req.GetResponse()
    return $res.StatusDescription
}

function Invoke-FtpDir {
    param(
        [parameter(Mandatory=$True)]$Connection, 
        [parameter(Mandatory=$False)]$Path = "/",
        [switch]$Detail
    )
    $req = New-FtpRequest @Connection -Path $Path
    if ($Detail) {
        $req.Method = [System.Net.WebRequestMethods+ftp]::ListDirectoryDetails
    }else{
        $req.Method = [System.Net.WebRequestMethods+ftp]::ListDirectory
    }
    $res = $req.GetResponse()
    $sr = New-Object System.IO.StreamReader($res.GetResponseStream())
    $ret = $sr.ReadToEnd()
    $sr.Dispose()
    return $ret
}

function Invoke-FtpMkdir {
    param(
        [parameter(Mandatory=$True)]$Connection, 
        [parameter(Mandatory=$True)]$Path
    )
    $req = New-FtpRequest @Connection -Path $Path
    $req.Method = [System.Net.WebRequestMethods+ftp]::MakeDirectory
    $res = $req.GetResponse()
    return $res.StatusDescription
}
function Invoke-FtpRmdir {
    param(
        [parameter(Mandatory=$True)]$Connection, 
        [parameter(Mandatory=$True)]$Path
    )
    $req = New-FtpRequest @Connection -Path $Path
    $req.Method = [System.Net.WebRequestMethods+ftp]::RemoveDirectory
    $res = $req.GetResponse()
    return $res.StatusDescription
}
function Invoke-FtpDel {
    param(
        [parameter(Mandatory=$True)]$Connection, 
        [parameter(Mandatory=$True)]$Path
    )
    $req = New-FtpRequest @Connection -Path $Path
    $req.Method = [System.Net.WebRequestMethods+ftp]::DeleteFile
    $res = $req.GetResponse()
    return $res.StatusDescription
}

function Invoke-FtpGet {
    param(
        [parameter(Mandatory=$True)]$Connection,
        [parameter(Mandatory=$True)]$Path,
        [parameter(Mandatory=$False)]$DestinationPath = "."
    )

    if (Test-Path $DestinationPath -PathType Container) {
        $FileName = $Path -replace '.*/',''
        $DestinationPath = Resolve-Path $DestinationPath
        $DestinationPath = Join-Path $DestinationPath $File
    }
    $req = New-FtpRequest @Connection -Path $Path
    $req.Method = [System.Net.WebRequestMethods+ftp]::DownloadFile

    $res = $req.GetResponse()
    $resStream = $res.GetResponseStream()
    $fs = New-Object system.io.filestream($DestinationPath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write)
    $resStream.CopyTo($fs)
    $fs.Dispose()
    $resStream.Dispose()
    return $res.StatusDescription
}

function Invoke-FtpBye {
    param([parameter(Mandatory=$True)]$Connection)
    $req = New-FtpRequest @Connection -Path "/"
    $req.Method = [System.Net.WebRequestMethods+ftp]::PrintWorkingDirectory
    $req.keepalive = $False
    $res = $req.GetResponse()
    return $res.ExitMessage
}

使い方

# 接続先情報をハッシュに詰め込みます
$con = @{
    ip = "192.168.1.26"
    user = "user"
    password = "password"
}

# 適当にテスト用のファイルを作ります
echo test > a.txt

# テスト用のファイルを PUT します
Invoke-FtpOpen  $con
Invoke-FtpDir   $con -Path "/"
Invoke-FtpMkdir $con -Path "/hoge/"
Invoke-FtpPut   $con -Path "/hoge/" -File a.txt
Invoke-FtpDir   $con -Path "/hoge/" -Detail

# テスト用のファイルを削除して GET します
rm a.txt
Invoke-FtpGet   $con -Path "/hoge/a.txt"
get-item a.txt

# 後片付け
Invoke-FtpDel   $con -Path "/hoge/a.txt"
Invoke-FtpRmdir $con -Path "/hoge"
Invoke-FtpBye   $con
rm a.txt

おわりに

リモートサーバ上のファイルサイズや更新日付を取得することもできるらしいのですが、伝統的な FTP の表示に寄せるデザインの才能が無かったので省略しました。テストもいい加減です。

参考にさせて頂きました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?