I want to use PowerShell to transfer files with FTP to an anonymous FTP server. I would not use any extra packages. How?
I am not sure you can 100% bullet proof the script from not hanging or crashing, as there are things outside your control (what if the server loses power mid-upload?) - but this should provide a solid foundation for getting you started:
# create the FtpWebRequest and configure it
$ftp = [System.Net.FtpWebRequest]::Create("ftp://localhost/me.png")
$ftp = [System.Net.FtpWebRequest]$ftp
$ftp.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$ftp.Credentials = new-object System.Net.NetworkCredential("anonymous","anonymous#localhost")
$ftp.UseBinary = $true
$ftp.UsePassive = $true
# read in the file to upload as a byte array
$content = [System.IO.File]::ReadAllBytes("C:\me.png")
$ftp.ContentLength = $content.Length
# get the request stream, and write the bytes into it
$rs = $ftp.GetRequestStream()
$rs.Write($content, 0, $content.Length)
# be sure to clean up after ourselves
$rs.Close()
$rs.Dispose()
There are some other ways too. I have used the following script:
$File = "D:\Dev\somefilename.zip";
$ftp = "ftp://username:password#example.com/pub/incoming/somefilename.zip";
Write-Host -Object "ftp url: $ftp";
$webclient = New-Object -TypeName System.Net.WebClient;
$uri = New-Object -TypeName System.Uri -ArgumentList $ftp;
Write-Host -Object "Uploading $File...";
$webclient.UploadFile($uri, $File);
And you could run a script against the windows FTP command line utility using the following command
ftp -s:script.txt
(Check out this article)
The following question on SO also answers this: How to script FTP upload and download?
I'm not gonna claim that this is more elegant than the highest-voted solution...but this is cool (well, at least in my mind LOL) in its own way:
$server = "ftp.lolcats.com"
$filelist = "file1.txt file2.txt"
"open $server
user $user $password
binary
cd $dir
" +
($filelist.split(' ') | %{ "put ""$_""`n" }) | ftp -i -in
As you can see, it uses that dinky built-in windows FTP client. Much shorter and straightforward, too. Yes, I've actually used this and it works!
Easiest way
The most trivial way to upload a binary file to an FTP server using PowerShell is using WebClient.UploadFile:
$client = New-Object System.Net.WebClient
$client.Credentials =
New-Object System.Net.NetworkCredential("username", "password")
$client.UploadFile(
"ftp://ftp.example.com/remote/path/file.zip", "C:\local\path\file.zip")
Advanced options
If you need a greater control, that WebClient does not offer (like TLS/SSL encryption, etc), use FtpWebRequest. Easy way is to just copy a FileStream to FTP stream using 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
$fileStream = [System.IO.File]::OpenRead("C:\local\path\file.zip")
$ftpStream = $request.GetRequestStream()
$fileStream.CopyTo($ftpStream)
$ftpStream.Dispose()
$fileStream.Dispose()
Progress monitoring
If you need to monitor an upload progress, you have to copy the contents by chunks yourself:
$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
$fileStream = [System.IO.File]::OpenRead("C:\local\path\file.zip")
$ftpStream = $request.GetRequestStream()
$buffer = New-Object Byte[] 10240
while (($read = $fileStream.Read($buffer, 0, $buffer.Length)) -gt 0)
{
$ftpStream.Write($buffer, 0, $read)
$pct = ($fileStream.Position / $fileStream.Length)
Write-Progress `
-Activity "Uploading" -Status ("{0:P0} complete:" -f $pct) `
-PercentComplete ($pct * 100)
}
$ftpStream.Dispose()
$fileStream.Dispose()
Uploading folder
If you want to upload all files from a folder, see
PowerShell Script to upload an entire folder to FTP
I recently wrote for powershell several functions for communicating with FTP, see https://github.com/AstralisSomnium/PowerShell-No-Library-Just-Functions/blob/master/FTPModule.ps1. The second function below, you can send a whole local folder to FTP. In the module are even functions for removing / adding / reading folders and files recursively.
#Add-FtpFile -ftpFilePath "ftp://myHost.com/folder/somewhere/uploaded.txt" -localFile "C:\temp\file.txt" -userName "User" -password "pw"
function Add-FtpFile($ftpFilePath, $localFile, $username, $password) {
$ftprequest = New-FtpRequest -sourceUri $ftpFilePath -method ([System.Net.WebRequestMethods+Ftp]::UploadFile) -username $username -password $password
Write-Host "$($ftpRequest.Method) for '$($ftpRequest.RequestUri)' complete'"
$content = $content = [System.IO.File]::ReadAllBytes($localFile)
$ftprequest.ContentLength = $content.Length
$requestStream = $ftprequest.GetRequestStream()
$requestStream.Write($content, 0, $content.Length)
$requestStream.Close()
$requestStream.Dispose()
}
#Add-FtpFolderWithFiles -sourceFolder "C:\temp\" -destinationFolder "ftp://myHost.com/folder/somewhere/" -userName "User" -password "pw"
function Add-FtpFolderWithFiles($sourceFolder, $destinationFolder, $userName, $password) {
Add-FtpDirectory $destinationFolder $userName $password
$files = Get-ChildItem $sourceFolder -File
foreach($file in $files) {
$uploadUrl ="$destinationFolder/$($file.Name)"
Add-FtpFile -ftpFilePath $uploadUrl -localFile $file.FullName -username $userName -password $password
}
}
#Add-FtpFolderWithFilesRecursive -sourceFolder "C:\temp\" -destinationFolder "ftp://myHost.com/folder/" -userName "User" -password "pw"
function Add-FtpFolderWithFilesRecursive($sourceFolder, $destinationFolder, $userName, $password) {
Add-FtpFolderWithFiles -sourceFolder $sourceFolder -destinationFolder $destinationFolder -userName $userName -password $password
$subDirectories = Get-ChildItem $sourceFolder -Directory
$fromUri = new-object System.Uri($sourceFolder)
foreach($subDirectory in $subDirectories) {
$toUri = new-object System.Uri($subDirectory.FullName)
$relativeUrl = $fromUri.MakeRelativeUri($toUri)
$relativePath = [System.Uri]::UnescapeDataString($relativeUrl.ToString())
$lastFolder = $relativePath.Substring($relativePath.LastIndexOf("/")+1)
Add-FtpFolderWithFilesRecursive -sourceFolder $subDirectory.FullName -destinationFolder "$destinationFolder/$lastFolder" -userName $userName -password $password
}
}
Here's my super cool version BECAUSE IT HAS A PROGRESS BAR :-)
Which is a completely useless feature, I know, but it still looks cool \m/ \m/
$webclient = New-Object System.Net.WebClient
Register-ObjectEvent -InputObject $webclient -EventName "UploadProgressChanged" -Action { Write-Progress -Activity "Upload progress..." -Status "Uploading" -PercentComplete $EventArgs.ProgressPercentage } > $null
$File = "filename.zip"
$ftp = "ftp://user:password#server/filename.zip"
$uri = New-Object System.Uri($ftp)
try{
$webclient.UploadFileAsync($uri, $File)
}
catch [Net.WebException]
{
Write-Host $_.Exception.ToString() -foregroundcolor red
}
while ($webclient.IsBusy) { continue }
PS. Helps a lot, when I'm wondering "did it stop working, or is it just my slow ASDL connection?"
You can simply handle file uploads through PowerShell, like this.
Complete project is available on Github here https://github.com/edouardkombo/PowerShellFtp
#Directory where to find pictures to upload
$Dir= 'c:\fff\medias\'
#Directory where to save uploaded pictures
$saveDir = 'c:\fff\save\'
#ftp server params
$ftp = 'ftp://10.0.1.11:21/'
$user = 'user'
$pass = 'pass'
#Connect to ftp webclient
$webclient = New-Object System.Net.WebClient
$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
#Initialize var for infinite loop
$i=0
#Infinite loop
while($i -eq 0){
#Pause 1 seconde before continue
Start-Sleep -sec 1
#Search for pictures in directory
foreach($item in (dir $Dir "*.jpg"))
{
#Set default network status to 1
$onNetwork = "1"
#Get picture creation dateTime...
$pictureDateTime = (Get-ChildItem $item.fullName).CreationTime
#Convert dateTime to timeStamp
$pictureTimeStamp = (Get-Date $pictureDateTime).ToFileTime()
#Get actual timeStamp
$timeStamp = (Get-Date).ToFileTime()
#Get picture lifeTime
$pictureLifeTime = $timeStamp - $pictureTimeStamp
#We only treat pictures that are fully written on the disk
#So, we put a 2 second delay to ensure even big pictures have been fully wirtten in the disk
if($pictureLifeTime -gt "2") {
#If upload fails, we set network status at 0
try{
$uri = New-Object System.Uri($ftp+$item.Name)
$webclient.UploadFile($uri, $item.FullName)
} catch [Exception] {
$onNetwork = "0"
write-host $_.Exception.Message;
}
#If upload succeeded, we do further actions
if($onNetwork -eq "1"){
"Copying $item..."
Copy-Item -path $item.fullName -destination $saveDir$item
"Deleting $item..."
Remove-Item $item.fullName
}
}
}
}
You can use this function :
function SendByFTP {
param (
$userFTP = "anonymous",
$passFTP = "anonymous",
[Parameter(Mandatory=$True)]$serverFTP,
[Parameter(Mandatory=$True)]$localFile,
[Parameter(Mandatory=$True)]$remotePath
)
if(Test-Path $localFile){
$remoteFile = $localFile.Split("\")[-1]
$remotePath = Join-Path -Path $remotePath -ChildPath $remoteFile
$ftpAddr = "ftp://${userFTP}:${passFTP}#${serverFTP}/$remotePath"
$browser = New-Object System.Net.WebClient
$url = New-Object System.Uri($ftpAddr)
$browser.UploadFile($url, $localFile)
}
else{
Return "Unable to find $localFile"
}
}
This function send specified file by FTP.
You must call the function with these parameters :
userFTP = "anonymous" by default or your username
passFTP = "anonymous" by default or your password
serverFTP = IP address of the FTP server
localFile = File to send
remotePath = the path on the FTP server
For example :
SendByFTP -userFTP "USERNAME" -passFTP "PASSWORD" -serverFTP "MYSERVER" -localFile "toto.zip" -remotePath "path/on/the/FTP/"
Goyuix's solution works great, but as presented it gives me this error: "The requested FTP command is not supported when using HTTP proxy."
Adding this line after $ftp.UsePassive = $true fixed the problem for me:
$ftp.Proxy = $null;
Simple solution if you can install curl.
curl.exe -p --insecure "ftp://<ftp_server>" --user "user:password" -T "local_file_full_path"
Related
Ive tried connecting to an ftp with the following powershell script:
#FTP Server Information - SET VARIABLES
$ftp = "ftp://XXX.com/"
$user = 'UserName'
$pass = 'Password'
$folder = 'FTP_Folder'
$target = "C:\Folder\Folder1\"
#SET CREDENTIALS
$credentials = new-object System.Net.NetworkCredential($user, $pass)
function Get-FtpDir ($url,$credentials) {
$request = [Net.WebRequest]::Create($url)
$request.Method = [System.Net.WebRequestMethods+FTP]::ListDirectory
if ($credentials) { $request.Credentials = $credentials }
$response = $request.GetResponse()
$reader = New-Object IO.StreamReader $response.GetResponseStream()
while(-not $reader.EndOfStream) {
$reader.ReadLine()
}
#$reader.ReadToEnd()
$reader.Close()
$response.Close()
}
#SET FOLDER PATH
$folderPath= $ftp + "/" + $folder + "/"
$files = Get-FTPDir -url $folderPath -credentials $credentials
$files
$webclient = New-Object System.Net.WebClient
$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
$counter = 0
foreach ($file in ($files | where {$_ -like "*.txt"})){
$source=$folderPath + $file
$destination = $target + $file
$webclient.DownloadFile($source, $target+$file)
#PRINT FILE NAME AND COUNTER
$counter++
$counter
$source
}
But i keep getting 530 error (authentication error) when connecting. I know the username and password is working cause i've tested it in other ftp clients.
So, i think it might be a problem because the webapp demands a certain protocol, which isnt used in this script.
Ive been doing some research and i found something called Posh-SSH which might work. But is there a way to modify my script instead? When im connecting with winscp i use FTP protocol with TLS/SSL implicit encryption to port 990.
UPDATE: WORKS
I made the following work:
#FTP Server Information - SET VARIABLES
$ftp = "ftp://waws-prod-xxx.ftp.azurewebsites.windows.net"
$user = 'xxxxx\xxxxx#email.com'
$pass = '$FRnqxxpxxxxxxx'
$folder = 'site/wwwroot/wwwroot/images/uploaded'
$target = "C:\Folder\Folder1\"
#SET CREDENTIALS
$credentials = new-object System.Net.NetworkCredential($user, $pass)
function Get-FtpDir ($url,$credentials) {
$request = [Net.WebRequest]::Create($url)
$request.Method = [System.Net.WebRequestMethods+FTP]::ListDirectory
if ($credentials) { $request.Credentials = $credentials }
$response = $request.GetResponse()
$reader = New-Object IO.StreamReader $response.GetResponseStream()
while(-not $reader.EndOfStream) {
$reader.ReadLine()
}
#$reader.ReadToEnd()
$reader.Close()
$response.Close()
}
#SET FOLDER PATH
$folderPath= $ftp + "/" + $folder + "/"
$files = Get-FTPDir -url $folderPath -credentials $credentials
Write-Host($files)
$files
$webclient = New-Object System.Net.WebClient
$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
$counter = 0
foreach ($file in ($files)){
$source=$folderPath + $file
$destination = $target + $file
$webclient.DownloadFile($source, $target+$file)
#PRINT FILE NAME AND COUNTER
$counter++
$counter
$source
}
I ended up adding a new credential to the web app, and changing sftp in hostname to ftp, and now it works. Using the credentials from webpublish file works too.
I also made WinSCP work and im able to download the full folder with children.
#FTP Server Information - SET VARIABLES
$ftp = "waws-prod-xxx-xxx.ftp.azurewebsites.windows.net"
$user = 'xxxxxx\xxxx#xxxxx.no'
$pass = '$xxxxxxxxx'
$folder = 'site/wwwroot/wwwroot/images/uploaded/*'
$target = "C:\Folder\Folder1\*"
# Load WinSCP .NET assembly
Add-Type -Path "WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Ftp
HostName = $ftp
UserName = $user
Password = $pass
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Download files
$session.GetFiles($folder, $target).Check()
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
FtpWebRequest (nor any other built-in .NET FTP API) does not support implicit TLS/SSL encryption.
See Does .NET FtpWebRequest Support both Implicit (FTPS) and explicit (FTPES)?
Posh-SSH is SSH/SFTP library. It has nothing to do with FTP.
And as you get an "authentication error", I believe your problem is actually different. Double check your credentials. You are probably using wrong ones in your code.
Also, if WinSCP works for you, you can use WinSCP .NET assembly from the PowerShell. WinSCP GUI can even generate a code template for you, based on your working GUI session.
Linux user here trying to write a script in PowerShell to automate SFTP transfers from a Windows server to a remote server. I have everything working for the most part (though I still have some testing to do). My current issue is after the script runs one time it is done. I need the FileSystemWatcher to run every time it finds a new file creation. I realize this is because of the exit but when I remove that portion and a user drops in a directory it loops through every file in the directory and uploads every file in the directory over and over. What I need it to do is run through the process once and then remain active to listen for another file creation event. My issue may be my limited workings with coding in Windows so any help is appreciated!
And yes I realize the duplication of code in the handling of my files vs my folders is redundant. I will most likely make that portion a function that accepts parameters depending on if it is given a file or folder.
I am posting my entire code in case it can help someone later
###Watches a directory for new files and fires off the batch file to push to Connexxion
$folder = "C:\pathTo\watchfolder"
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $folder
$watcher.Filter = "*.csv"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true
### LISTEN FOR CREATE
Register-ObjectEvent $watcher Created -SourceIdentifier FileCreated -Action {
$folderpath = $(split-path -LiteralPath $event.SourceEventArgs.FullPath)
$filename = $event.SourceEventArgs.Name
$filepath = (Join-Path $folderpath $filename.split("\")[-1])
Write-Host folderpath: $folderpath
Write-Host filename: $filename
Write-Host filepath: $filepath
$remotePath = "/data/"
If($filename.contains("\")){
Write-Host "Here is directory"
try
{
#Set path to winscp
$assemblyPath = "C:\Program Files (x86)\WinSCP"
Add-Type -Path (Join-Path $assemblyPath "WinSCPnet.dll")
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Sftp
HostName = "hostname"
UserName = "username"
Password = "passwd"
SshHostKeyFingerprint = "ssh-rsa 2048 xx:xx"
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Upload files, collect results
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
$transferResult =
$session.PutFiles($folderpath, $remotePath, $False, $transferOptions)
# Throw on any error
$transferResult.Check()
# Print results
foreach ($transfer in $transferResult.Transfers)
{
Write-Host "Upload of $($transfer.FileName) succeeded"
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
exit 0
}#end of first try
catch
{
Write-Host "Error: $($_.Exception.Message)"
exit 1
}
}
Else{
Write-Host "Here is a file"
try
{
#Set path to winscp
$assemblyPath = "C:\Program Files (x86)\WinSCP"
Add-Type -Path (Join-Path $assemblyPath "WinSCPnet.dll")
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Sftp
HostName = "hostname"
UserName = "username"
Password = "passwd"
SshHostKeyFingerprint = "ssh-rsa 2048 xx:xx"
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Upload files, collect results
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
$transferResult =
$session.PutFiles($filepath, $remotePath, $False, $transferOptions)
# Throw on any error
$transferResult.Check()
# Print results
foreach ($transfer in $transferResult.Transfers)
{
Write-Host "Upload of $($transfer.FileName) succeeded"
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
exit 0
}#end of first try
catch
{
Write-Host "Error: $($_.Exception.Message)"
exit 1
}
}
}#end of action
while ($true) {sleep 5}
Just take the path to the new file and use RemotePath.TranslateLocalPathToRemote to map it to a server path, and upload the file there.
The code will be a lot simpler. All you need to do is to make sure new folders are re-created on the server.
# Watches a directory for new files and
# fires off the batch file to push to connection
$remotePath = "/data"
$localPath = "C:\pathTo\watchfolder"
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $localPath
$watcher.Filter = "*.csv"
$watcher.IncludeSubdirectories = $True
$watcher.EnableRaisingEvents = $True
### LISTEN FOR CREATE
Register-ObjectEvent $watcher Created -SourceIdentifier FileCreated -Action {
try
{
$localFilePath = $event.SourceEventArgs.FullPath
Write-Host "Local path: $localFilePath"
$assemblyPath = "C:\Program Files (x86)\WinSCP"
Add-Type -Path (Join-Path $assemblyPath "WinSCPnet.dll")
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Sftp
HostName = "example.com"
UserName = "username"
Password = "password"
SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxxxxxxx...="
}
$session = New-Object WinSCP.Session
try
{
$remoteFilePath =
[WinSCP.RemotePath]::TranslateLocalPathToRemote(
$localFilePath, $localPath, $remotePath)
Write-Host "Remote path: $remoteFilePath"
# Connect
$session.Open($sessionOptions)
# Check if corresponding remote directory exists, if not, create it
$i = $remoteFilePath.LastIndexOf("/")
$remoteDirPath = $remoteFilePath.SubString(0, $i)
if (($remoteDirPath.Length -gt 0) -and
!$session.FileExists($remoteDirPath))
{
Write-Host "New subdirectory, creating $remoteDirPath on server"
$session.CreateDirectory($remoteDirPath)
}
$session.PutFiles($localFilePath, $remoteFilePath).Check()
Write-Host "Upload of $localFilePath succeeded"
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
} #end of first try
catch
{
Write-Host "Error: $($_.Exception.Message)"
}
} #end of action
while ($True) { sleep 5 }
Hi All i am new to power shell scripting, i am trying to download all files in root directory and files in sub folder based on file name from FTP server. As i am looking to download files which got matched on files names in like, as my path will always point to root directory and root directory is having text files and sub folders, the sub folders contains text files.
I found an example to get text files to download to local from ftp server, but the example i found is only downloading text files from root directory it is not scanning sub folders and not getting files in sub folders.
Here is the example i tried
# $url = "ftp://XXX.com/"
$user = 'UserName'
$pass = 'Password'
$folder = 'FTP_Folder'
$target = "C:\Folder\Folder1\"
#SET CREDENTIALS
$credentials = new-object System.Net.NetworkCredential($user, $pass)
function Get-FtpDir ($url,$credentials) {
$request = [Net.WebRequest]::Create($url)
$request.Method = [System.Net.WebRequestMethods+FTP]::ListDirectory
if ($credentials) { $request.Credentials = $credentials }
$response = $request.GetResponse()
$reader = New-Object IO.StreamReader $response.GetResponseStream()
$reader.ReadToEnd()
$reader.Close()
$response.Close()
}
#SET FOLDER PATH
$folderPath= $ftp + "/" + $folder + "/"
$Allfiles=Get-FTPDir -url $folderPath -credentials $credentials
$files = ($Allfiles -split "`r`n")
$files
$webclient = New-Object System.Net.WebClient
$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
$counter = 0
foreach ($file in ($files | where {$_ -like "*.txt"})){
$source=$folderPath + $file
$destination = $target + $file
$webclient.DownloadFile($source, $target+$file)
#PRINT FILE NAME AND COUNTER
$counter++
$counter
$source
}
I tried a module PSFTP module functions like Get-ftpchilditem function with recursive, but it not worked for me.
Please help me in this to get files from root folder and sub folders of FTP server to local system.
I have done it in the past using WinSCP and SFTP. The example is below. This uses SFTP SCP which is basically over SSH. I am sure you can change the WInSCP protocol to ftp and try different combination. Hope this helps you. Change the FileMasks to only include .txt and this will go through sub folders. Also for this to work you will require WinSCPnet.dll and WinSCP.exe which you can easily download from WinSCP site. Download the ".Net Assembly/COM Library" and keep the script and the required files in the same folder and update the path in the script. Good Luck.
# Load WinSCP .NET assembly
Add-Type -Path "C:\Users\administrator.AD\Documents\WinSCP Script\WinSCPnet.dll"
$hostname = "ftp.xyz.net"
$user = "me"
$pass = "mypassword"
$sfolder = "/home/me"
$dfolder = "C:\tmp"
$sshHostKeyFingerprint = "ssh-rsa 2048 eb:2c:f9:ab:19:2e:0c:22:02:d4:8f:64:44:75:ec:04"
$mask = "*.jpg;*.jpeg;*.png;*.tiff;*.txt"
####################### END USER CONFIGURABLE SETTINGS #########################
# Main script
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Scp
HostName = $hostname
UserName = $user
Password = $pass
SshHostKeyFingerprint = $sshHostKeyFingerprint
}
$session = New-Object WinSCP.Session
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.FileMask = "*.jpg; *.jpeg; *.png; *.tiff;"
try
{
# Will continuously report progress of synchronization
$session.add_FileTransferred( { FileTransferred($_) } )
# Connect
$session.Open($sessionOptions)
# Synchronize files
$synchronizationResult = $session.SynchronizeDirectories(
[WinSCP.SynchronizationMode]::Local,$dfolder, $sfolder, $False, $False, 1, $transferOptions)
# Throw on any error
$synchronizationResult.Check()
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
I want to run a PS script when I want to publish to FTP server. I took this script as structure : structure script.
I have very simple folder :
C:\Uploadftp\Files\doc.txt
C:\Uploadftp\Files\Files2
C:\Uploadftp\Files\Files2\doc2.txt
nothing fancy there.
Here is my script :
cd C:\Uploadftp
$location = Get-Location
"We are here: $location"
$user = "test" # Change
$pass = "test" # Change
## Get files
$files = Get-ChildItem -recurse
## Get ftp object
$ftp_client = New-Object System.Net.WebClient
$ftp_client.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
$ftp_address = "ftp://test/TestFolder"
## Make uploads
foreach($file in $files)
{
$directory = "";
$source = $($file.DirectoryName + "/" + $file);
if ($file.DirectoryName.Length -gt 0)
{
$directory = $file.DirectoryName.Replace($location,"")
}
$directory = $directory.Replace("\","/")
$source = $source.Replace("\","/")
$directory += "/";
$ftp_command = $($ftp_address + $directory + $file)
# Write-Host $source
$uri = New-Object System.Uri($ftp_command)
"Command is " + $uri + " file is $source"
$ftp_client.UploadFile($uri, $source)
}
I keep getting this error :
Exception calling "UploadFile" with "2" argument(s): "An exception occurred during a WebClient request."
If I hardcode specific folder for $uri and tell source to be some specific folder on my computer, this script doesn't create directory, it creates a file. What am I doing wrong?
P.S. dont hit me too hard, its my fist time ever doing something in power shell.
Try the "Create-FtpDirectory" function from https://github.com/stej/PoshSupport/blob/master/Ftp.psm1
function Create-FtpDirectory {
param(
[Parameter(Mandatory=$true)]
[string]
$sourceuri,
[Parameter(Mandatory=$true)]
[string]
$username,
[Parameter(Mandatory=$true)]
[string]
$password
)
if ($sourceUri -match '\\$|\\\w+$') { throw 'sourceuri should end with a file name' }
$ftprequest = [System.Net.FtpWebRequest]::Create($sourceuri);
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::MakeDirectory
$ftprequest.UseBinary = $true
$ftprequest.Credentials = New-Object System.Net.NetworkCredential($username,$password)
$response = $ftprequest.GetResponse();
Write-Host Upload File Complete, status $response.StatusDescription
$response.Close();
}
So in my powershell script when it starts up it polls a ftp server and downloads any files that aren't in the local folder. The problem is when it gets to a folders it downloads them as files. this is my code for checking for new files:
$LocFolder = 'C:\EMSDropBox\*'
Remove-Item $LocFolder
$ftprequest = [System.Net.FtpWebRequest]::Create("ftp://NZHQFTP1/tbarnes")
$ftprequest.Proxy = $null
$ftprequest.KeepAlive = $false
$ftprequest.TimeOut = 10000000
$ftprequest.UsePassive = $False
$ftprequest.Credentials = New-Object System.Net.NetworkCredential("tbarnes", "Static_flow2290")
$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectory
$FTPResponse = $ftprequest.GetResponse()
$ResponseStream = $FTPResponse.GetResponseStream()
$FTPReader = New-Object System.IO.Streamreader($ResponseStream)
$filename = $FTPReader.ReadLine()
while($filename -ne $null)
{
try
{
if((Test-Path ("C:\emsdropbox\"+$filename)) -ne $true)
{
downloadFtp($filename)
}
$filename = $FTPReader.ReadLine()
}
catch
{
Write-Host $_
}
}
$FTPReader.Close()
$FTPResponse.Close()
$ResponseStream.Close()
and this is the downloadFtp function:
# FTP Config
$FTPHost = "****"
$Username = "******"
$Password = "*********"
$FTPFile = $file
# FTP Log File Url
$FTPFileUrl = "ftp://" + $FTPHost + "/tbarnes/" + $FTPFile
# Create FTP Connection
$FTPRequest = [System.Net.FtpWebRequest]::Create("$FTPFileUrl")
$FTPRequest.Credentials = New-Object System.Net.NetworkCredential($Username, $Password)
$FTPRequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
$FTPRequest.UsePassive = $false
$FTPRequest.UseBinary = $true
$FTPRequest.KeepAlive = $false
$targetfile = New-Object IO.FileStream (("C:\emsdropbox\"+$file),[IO.FileMode]::Create)
# Get FTP File
$FTPResponse = $FTPRequest.GetResponse()
$ResponseStream = $FTPResponse.GetResponseStream()
$FTPReader = New-Object -typename System.IO.StreamReader -ArgumentList $ResponseStream
[byte[]]$readbuffer = New-Object byte[] 1024
#loop through the download stream and send the data to the target file
do{
$readlength = $ResponseStream.Read($readbuffer,0,1024)
$targetfile.Write($readbuffer,0,$readlength)
}
while ($readlength -ne 0)
$FTPReader.Close()
Im not sure why it wont pull them down as folders so any help or pointers would be great!
The FTP methods don't support downloading of folders, or recursion, by themselves, so there's no other way I can think of doing this but what I've suggested below.
Change the method so you can differentiate between files and directories and handle them accordingly.
Change $ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectory to $ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectoryDetails
Which will list in this format:
-rwxrwxrwx 1 owner group 277632 Mar 4 17:15 xml_socket.log.rar
Directories will have a d in place of the - at the start of the line.
Here is an amended try block that will match only files so you can pass to the downloadFtp function:
try
{
if(!($filename -match '^d')){if((Test-Path ("C:\emsdropbox\"+$filename.Split(" ")[8])) -ne $true)
{
downloadFtp(($filename -split '\s+')[8])
}}
$filename = $FTPReader.ReadLine()
}
If you want to then get a list of directories, use the following try block against the same ftpresponse stream and for each, ListDirectoryDetails to get the list of files in each directory to process them:
try
{
if($filename -match '^d'){if((Test-Path ("C:\emsdropbox\"+$filename.Split(" ")[8])) -ne $true)
{
listdir(($filename -split '\s+')[8])
}}
$filename = $FTPReader.ReadLine()
}
You may also have to create the local directories too which you can do in powershell as follows:
New-Item c:\emsdropbox\newdir -type directory