Загрузка Powershell ftps на box.com в пассивном режиме
Эта проблема:
Клиент требует, чтобы мы загружали извлеченные данные из нашей системы на их платформу box.com, а не в нашу обычную утилиту SFTP. У меня есть учетные данные box.com, и я знаю, что они требуют FTPS, а не SFTP, и требуют пассивного режима. Я написал фрагмент из сценария загрузки и скачивания FTP-сервера от Powershell от ThomasMaurer. Версия Powershell на моем сервере 4.0
Фрагмент кода:
#config
$Username = "username@host.com"
$Password = "redactedpassword"
$LocalFile = "C:\path\to\my\file.csv"
$RemoteFile = "ftp://ftp.box.com:990/file.csv"
#Create FTPWebRequest
$FTPRequest = [System.Net.FtpWebRequest]::Create($RemoteFile)
$FTPRequest = [System.Net.FtpWebRequest]$FTPRequest
$FTPRequest.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$FTPRequest.Credentials = New-Object System.Net.NetworkCredential($Username, $Password)
$FTPRequest.UseBinary = $true
$FTPRequest.UsePassive = $true
#read file for upload
$FileContent = gc -en byte $LocalFile
$FTPRequest.ContentLength = $FileContent.Length
#get stream request by bytes
$run = $FTPRequest.GetRequestStream()
$run.Write($FileContent,0,$FileContent.Length)
#cleanup
$run.Close()
$run.Dispose()
Ошибка (и):
Exception calling "GetRequestStream" with "0" argument(s): "System error." At C:\path\to\my\powershellscript.ps1:28 char:1
+ $Run = $FTPRequest.GetRequestStream()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: () [], MethodInvocationException
+ FullyQualifiedErrorId: WebException
Я также получаю последующие ошибки при вызове свойства $FileContent.Length и $run.close и $run.dispose()
Кто-нибудь успешно автоматизировал коробку (в частности) или пассивную implicit-ssl, используя только команды powershell 4.0, и у вас есть надежный шаблон, который я мог бы использовать повторно? Большое спасибо
2 ответа
Я загружаю файлы с производной версией System.Net.WebClient, которая поддерживает FTP через TLS. Этого легко достичь, внедрив код C# в PowerShell:
$typeDefinition = @"
using System;
using System.Net;
public class FtpClient : WebClient
{
protected override WebRequest GetWebRequest(Uri address)
{
FtpWebRequest ftpWebRequest = base.GetWebRequest(address) as FtpWebRequest;
ftpWebRequest.EnableSsl = true;
return ftpWebRequest;
}
}
"@
Add-Type -TypeDefinition $typeDefinition
$ftpClient = New-Object FtpClient
$ftpClient.UploadFile("ftp://your-ftp-server/yourfile.name", "STOR", "C:\YourLocalFile.name")
Возможно, слишком поздно, чтобы быть полезным для оригинального спрашивающего, но я нашел, что этот другой ответ помог мне: ответ Сирила Гупты на "Загрузка файлов с помощью ftp с помощью powershell"
Вот мое пересмотренное издание, включая кодировку URL (поскольку имена пользователей box.com - это адреса электронной почты, которые включают знак "at"):
## https://stackru.com/a/2485696/537243
## User comment complains can't turn off passive mode,
## but that is exactly what we want here!
[Reflection.Assembly]::LoadWithPartialName("System.Web") | Out-Null
# config
$Username = "foo@bar.com"
$Password = "s3cr3tpAssw0rd"
$Servername = "ftp.box.com"
# This is what we need URI it to look like:
# ftp://foo%40bar.com:s3cr3tpAssw0rd@ftp.box.com/
$baseURI = "ftp://$([System.Web.HttpUtility]::UrlEncode($Username)):$([System.Web.HttpUtility]::UrlEncode($Password))@$($Servername)"
$LocalFile = "C:\tmp\to_upload\data.csv"
$RemoteFile = "date.csv"
$ftpURI = "$($baseURI)/$($RemoteFile)"
Write-output "ftp uri: $($ftpURI)";
$webclient = New-Object -TypeName System.Net.WebClient;
$ftpURI = New-Object -TypeName System.Uri -ArgumentList $ftpURI; #"convert" it
$webclient.UploadFile($ftpURI, $LocalFile);
Write-output "Uploaded $($LocalFile) ... "; # of course since we didn't use try/catch or other error dectection this is a bit presuming.
Также следует отметить, что в этом примере используется обычный FTP, а не FTPS или SFTP.
Ответ @h0r41i0 решает проблему, используя WebClient
. Но какWebClient
внутренне использует (Ftp)WebRequest
, это не может быть решением само по себе.
Я предполагаю, что "Системная ошибка" возникает из-за того, что любой OP пытается подключиться к безопасному порту (990) с незащищенным соединением.
Или потому, что файл слишком велик и код OP пытается прочитать его целиком в память:
$FileContent = gc -en byte $LocalFile
В любом случае нет причин отказываться от FtpWebRequest
. Просто используйте безопасное соединение ( FtpWebRequest.EnableSsl
). И эффективный способ передать данные из файла в поток FTP, например Stream.CopyTo
:
$request = [Net.WebRequest]::Create("ftp://ftp.example.com/remote/path/file.zip")
$request.Credentials = New-Object System.Net.NetworkCredential("username", "password")
$request.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$request.EnableSsl = $True
$fileStream = [System.IO.File]::OpenRead("C:\local\path\file.zip")
$ftpStream = $request.GetRequestStream()
$fileStream.CopyTo($ftpStream)
$ftpStream.Dispose()
$fileStream.Dispose()
Другие параметры см. В разделе Отправка файлов по FTP с помощью PowerShell.
Однако обратите внимание, что.NET framework не поддерживает неявный TLS (что является типичным использованием 990). Только явный TLS. Но в любом случае поддержка явного TLS встречается чаще. См. Поддерживает ли.NET FtpWebRequest как неявный (FTPS), так и явный (FTPES)?