I am working on a PowerShell script that is run from another application that is called after a success event and executes a series of commands:
First it zips a designated file (ExampleFile)
Then deleted the original
Uploads the zipped file to a server through FTP
My problem starts during the FTP process: as it exists below - the ZIP file is created correctly in the local system, and a ZIP file is created on the FTP server but it's stuck with a filesize of 0 bytes.
From the server, it looks like the upload hangs?
So: Lines 1-3 all work fine and the local zipped file has a non-zero size.
Add-Type -A 'System.IO.Compression.FileSystem';
[IO.Compression.ZipFile]::CreateFromDirectory("ExampleFolder\ExampleFile", "ExampleFolder\ExampleFile_Datestamp.zip");
Remove-Item -Recurse -Force "ExampleFolder\ExampleFile";
$Username = "exampleusername";
$Password = "examplepassword";
$LocalFile = "C:\Users\example_path\ExampleFolder\ExampleFile.zip";
$RemoteFile = "ftp://exampleserver/examplepath2/ExampleFile_Datestamp.zip";
$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;
$FileContent = gc -en byte $LocalFile;
$FTPRequest.ContentLength = $FileContent.Length;
$Run = $FTPRequest.GetRequestStream();
$Run.Write($FileContent, 0, $FileContent.Length);
$Run.Close();
$Run.Dispose();
This has me pretty well stumped, so any ideas or thoughts appreciated.
powershell.exe -nologo -noprofile -command "Add-Type -A 'System.IO.Compression.FileSystem'; [IO.Compression.ZipFile]::CreateFromDirectory(\"ExampleFolder\ExampleFile\", \"ExampleFolder\ExampleFile_Datestamp.zip\"); Remove-Item -Recurse -Force \"ExampleFolder\ExampleFile\"; $Username = \"exampleusername\"; $Password = \"examplepassword\"; $LocalFile = \"C:\Users\example_path\ExampleFolder\ExampleFile.zip\"; $RemoteFile = \"ftp://exampleserver/examplepath2/ExampleFile_Datestamp.zip\"; $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; $FileContent = gc -en byte $LocalFile; $FTPRequest.ContentLength = $FileContent.Length; $Run = $FTPRequest.GetRequestStream(); $Run.Write($FileContent, 0, $FileContent.Length); $Run.Close(); $Run.Dispose();
You are missing a call to FtpWebRequest.GetResponse:
...
$Run = $FTPRequest.GetRequestStream();
$Run.Write($FileContent, 0, $FileContent.Length);
$Run.Close();
$FTPResponse = $FTPRequest.GetResponse()
Write-Out $FTPResponse.StatusCode
Write-Out $FTPResponse.StatusDescription
See How to: Upload Files with FTP.
Related
Not able transfer multiple file though inputted correct path
######Transfer file
Write-Host ("Start script.")
try
{
$todaysDate = (Get-Date).ToString('yyyy-MM-dd')
Add-Type -Path "D:\WinSCP\WinSCPnet.dll"
# Setup session options
$session = New-Object WinSCP.Session
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.Protocol = [WinSCP.Protocol]::Sftp
$SessionOptions.Timeout = New-TimeSpan -Seconds 90
$sessionOptions.HostName = "hostname"
$sessionOptions.UserName = "username"
$sessionOptions.PortNumber = "portnumber"
$sessionOptions.Password = ""
$sessionOptions.SshPrivateKeyPath = "D:\privatekey"
$sessionOptions.SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxx"
#Write-Host ("Connecting.")
$session = New-Object WinSCP.Session
$session.SessionLogPath = "D:\WinSCPSessionLog_$todaysDate.log"
#Upload files
try
{
# Connect
$session.Open($sessionOptions)
#File list:
Write-Host ("File list: ")
#transferoptions
$transferOptions = New-Object WinSCP.TransferOptions
#$transferOptions.FileMask = "*.*"
$transferOptions.FilePermissions = $Null # This is default
$transferOptions.PreserveTimestamp = $False # if Timestamp on file is enable
$localPath = Get-ChildItem "D:\dq\*.csv" | Where-Object {($_.LastWriteTime -ge [datetime]::today)}
$remotePath = "/Outbox/"
# Upload files, collect results
$transferResult = $session.PutFiles($localPath, $remotePath, $False, $transferOptions)
# Upload files, collect results
#$transferResult = $session.PutFiles(($localPath + "*.*"), ($remotePath, + "*.*") $False, $transferOptions).Check()
# Iterate over every transfer
foreach ($transfer in $transferResult.Transfers)
{
# Success or error?
if ($transfer.Error -eq $Null)
{
#$transferResult = $session.PutFiles($localPath, $remotePath, $False, $transferOptions).Check()
Write-Host ("Upload of {0} succeeded, moving to save" -f $transfer.FileName)
}
else
{
Write-Host ("Upload of {0} failed: {1}" -f $transfer.FileName, $transfer.Error.Message)
}
}
#End of files:
Write-Host ("End of files. ")
}
finally
{
# Disconnect, clean up
$session.Dispose()
Write-Host ("Disconnected.")
}
#exit 0
}
catch [Exception]
{
$todaysDate = (Get-Date).ToString('yyyy-MM-dd')
Set-Content -Path "D:\WinSCPError_$todaysDate.log" $_.Exception.Message
#exit 1
}
Log File:
File list:
Upload of D:\dq\ADD.csv D:\dq\MINUS.csv D:\dq\DIVIDE.csv failed: File or folder 'D:\dq\ADD.csv D:\dq\MINUS.csv D:\dq\DIVIDE.csv' does not exist.
System Error. Code: 123.
The filename, directory name, or volume label syntax is incorrect
Error:
not able transfer file due to file not exists. I have inputted correct
Tools:
Using superb old version of .NET
Using superb old version of window server
Expected result:
able transfer multiple files in "D:\dq*.csv" and the file modified date must be today's date
I have inputted correct
Nope, you haven't. From the WinSCP documentation for the localPath argument:
Full path to local file or directory to upload. Filename in the path can be replaced with Windows wildcard1 to select multiple files. To upload all files in a directory, use mask *.
That is, it expects one path, not multiple.
Given your requirement for filtering on LastWriteTime, passing "D:\dq\*.csv" as the argument is not a viable option, so the solution is to upload the files one-by-one:
# ...
try{
# Connect
$session.Open($sessionOptions)
#File list:
Write-Host ("File list: ")
#transferoptions
$transferOptions = New-Object WinSCP.TransferOptions
#$transferOptions.FileMask = "*.*"
$transferOptions.FilePermissions = $Null # This is default
$transferOptions.PreserveTimestamp = $False # if Timestamp on file is enable
# Loop through each relevant file
foreach($localFile in Get-ChildItem "D:\dq\*.csv" | Where-Object {($_.LastWriteTime -ge [datetime]::today)}){
$remotePath = "/Outbox/"
# Upload files, collect results
$transferResult = $session.PutFileToDirectory($localFile.FullName, $remotePath, $false, $transferOptions)
# Success or error?
if ($transferResult.Error -eq $Null) {
#$transferResult = $session.PutFiles($localPath, $remotePath, $False, $transferOptions).Check()
Write-Host ("Upload of {0} succeeded, moving to save" -f $transferResult.FileName)
}
else {
Write-Host ("Upload of {0} failed: {1}" -f $transferResult.FileName, $transferResult.Error.Message)
}
}
}
finally{
# ...
}
The answer by #Martias is correct. You cannot pass multiple paths to Session.PutFiles.
But you can let WinSCP itself pick the today's *.csv files with just two lines of code, using todays constraint.
Using Session.PutFilesToDirectory would also make the code somewhat simpler.
$transferOptions = New-Object WinSCP.TransferOptions
# (your other transfer options)
$transferOptions.FileMask = ">=today"
$transferResult =
$session.PutFilesToDirectory(
$localPath, $remotePath, "*.csv", $False, $transferOptions).Check()
I am facing this situation where, when i run my powershell script from PowershellISE it works as desired. But when the same script is called from a batch file the part of the powershell script with a progressbar function is written, it shows no response. I am unable to find out what is it that i am missing.
The powershell script is :
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") | Out-Null
#FUNCTION- progress bar
function progress-bar {
$Runspace = [runspacefactory]::CreateRunspace()
$PowerShell = [System.Management.Automation.PowerShell]::Create()
$PowerShell.runspace = $Runspace
$Runspace.Open()
[void]$PowerShell.AddScript({
$Form = New-Object System.Windows.Forms.Form
$Form.width = 1000
$Form.height = 200
$Form.Text = "text"
$Form.Font = New-Object System.Drawing.Font("Times New Roman" ,12, [System.Drawing.FontStyle]::Regular)
$Form.MinimizeBox = $False
$Form.MaximizeBox = $False
$Form.WindowState = "Normal"
$Form.StartPosition = "CenterScreen"
$ProgressBar = New-Object System.Windows.Forms.ProgressBar
$ProgressBar.Maximum = 100
$ProgressBar.Minimum = 0
$ProgressBar.Location = new-object System.Drawing.Size(10,70)
$ProgressBar.size = new-object System.Drawing.Size(967,10)
$ProgressBar.style = 'Marquee'
$MessagesLabel = New-Object System.Windows.Forms.Label
$MessagesLabel.AutoSize = $true
$MessagesLabel.Location = New-Object System.Drawing.Point(10,45)
$MessagesLabel.Text = "message for user"
$ShownFormAction = {
$Form.Activate()
# $Form.Dispose()
}
$Form.Add_Shown($ShownFormAction)
$Form.Controls.Add($MessagesLabel)
$Form.Controls.Add($ProgressBar)
$Form.ShowDialog()
})
$PowerShell.BeginInvoke()
}
#begin main process
progress-bar
Get-Date
Start-Sleep 5
#end main process
and the batch file which i am using to call this above script contains:
echo off
SET HomeDir=%~dp0
SET PowerShellScriptPath=%HomeDir%
PowerShell -NoProfile -ExecutionPolicy Bypass -Command "& '%HomeDir%\1.ps1'";
exit
Please help with any pointer to solution.
Try using the start command in the batch script e.g.
start powershell.exe -NoProfile -ExecutionPolicy Bypass -Command "& '%HomeDir%\1.ps1'";
This will open the powershell process in a new window and close the batch behind it, hopefully ensuring that whatever features you're using should display properly.
I have a question, last saturday when I was migrate DC from Windows 2003 to 2012R2, everything was migrate ok, but I can't run a ps script on Windows 2012R2. I change some parameter, mail adress etc.
param([String]$EmailTo = "user#domain.com")
psql.exe -h 10.1.1.20 -p 5432 -w -H -f C:\Scripts\errors_o_htm_V3.sql' -o 'C:\Scripts\errors.html -U script database
$file = "C:\Scripts\errors.html"
$old = Get-Content $file
Add-Content -Path $file -Value $old
$EmailFrom = "sender#domain.com"
$Subject = "Reports"
$SMTPServer = "smtp.serwer.com"
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587)
$SMTPClient.EnableSsl = $false
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("user","password");
$msg = new-object Net.Mail.MailMessage
$msg.From = $EmailFrom
$msg.To.Add($EmailTo)
$msg.To.Add("user#domain.com")
$msg.Subject = $Subject
$msg.IsBodyHTML = $True
$msg.Body = $old
$msg.BodyEncoding = [System.Text.Encoding]::GetEncoding("UTF-8")
$SMTPClient.Send($msg)
When I run script manualy, he generate error file and send it, but when I want to set it in scheduler - he send an empty mail, something wrong with psql.exe -h ......
I add psql path to Environment Variables.
In scheduler i set:
Program/Script powershell.exe or full path
Add arguments C:\Scripts\script.ps1, and also I add -f before path.
But when I run this script he don't generate the error file with data.
I thing something is wrong with shedule parameters.
Hoping someone can guide me / help me.
The issue, I have 2 servers one running a Ubuntu which has a website for clients to login and download / view reports. The other is a windows server 2012 R2 which creates / stores the reports. I need to move the files from the windows to the Ubuntu server so clients can view. The data is large currently 7gb and growing at 3 gb a year.
I need a batch file to connect using ftp and then copy the folder to a local folder. However it only needs to copy those files which have modified.
I have only ever written one batch file and I cant seem to find any ftp batch scripts which only copies modifed files.
Your my last resort as I cant seem to find a coder who knows batch script (its a dieing art). I have never used powershell so would not know where to start here.
Any help or advice please let me know.
Thanks
John
You can do it with PowerShell with winscp. Exemple :
try
{
# Load WinSCP .NET assembly
Add-Type -Path "WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property #{
Protocol = [WinSCP.Protocol]::Sftp
HostName = "example.com"
UserName = "user"
Password = "mypassword"
SshHostKeyFingerprint = "ssh-rsa 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Upload files
$transferOptions = New-Object WinSCP.TransferOptions
$transferOptions.TransferMode = [WinSCP.TransferMode]::Binary
$transferResult = $session.PutFiles("d:\toupload\*", "/home/user/", $False, $transferOptions)
# Throw on any error
$transferResult.Check()
# Print results
foreach ($transfer in $transferResult.Transfers)
{
Write-Host ("Upload of {0} succeeded" -f $transfer.FileName)
}
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
exit 0
}
catch [Exception]
{
Write-Host ("Error: {0}" -f $_.Exception.Message)
exit 1
}
This would be a way to do it in PowerShell. It would take files that are older then 31 days and upload them.
function FTP-Upload {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Source_File,
[Parameter(Mandatory=$true)]
[string]$Target_File,
[Parameter(Mandatory=$true)]
[string]$Target_Server,
[Parameter(Mandatory=$true)]
[string]$Target_Username,
[Parameter(Mandatory=$true)]
[string]$Target_Password
)
$FTP = [System.Net.FTPWebRequest]::Create("ftp://$Target_Server/$Target_File")
$FTP = [System.Net.FTPWebRequest]$FTP
$FTP.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$FTP.Credentials = New-Object System.Net.NetworkCredential($Target_Username,$Target_Password)
$FTP.UseBinary = $true
$FTP.UsePassive = $true
# read in the file to upload as a byte array
$content = [System.IO.File]::ReadAllBytes($Source_File)
$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()
}
$Upload_Server = "server.network.tld"
$Upload_Location = "/data/"
$Upload_Username = "ftpuser"
$Upload_Password = "ftppassword"
$Files_To_Upload = Get-ChildItem E:\Path\To\Files -Recurse | Where-Object {($_.CreationTime -le (Get-Date).AddDays(-31)) -and (!$_.PSIsContainer)}
Foreach ($File in $Files_To_Upload) {
FTP-Upload -Source_File $File.FullName -Target_File ($Upload_Location + $File.Name) -Target_Server $Upload_Server -Target_Username $Upload_Username -Target_Password $Upload_Password
}
I have a simple Java client & server pair (here called FixedMessageSequenceClient and FixedMessageSequenceServer). I want the output from both of them to go to the same CMD window. However, as it is they go to separate Windows. Here is my code so far:
$serviceStart = New-Object System.Diagnostics.ProcessStartInfo
$serviceStart.RedirectStandardInput = $true
$serviceStart.UseShellExecute = $false; #needed to redirect console input
#$serviceStart.FileName = ""
$serviceStart = Start-Process java FixedMessageSequenceServer
$serviceStart.WorkingDirectory = "C:\Java_Scratch2\FixedMessageSequenceServer.java"
$serviceStart.Arguments = "AsConsole"
$serviceStart = [System.Diagnostics.Process]::Start($serviceStart)
$service2Start = New-Object System.Diagnostics.ProcessStartInfo
$service2Start.RedirectStandardInput = $true
$service2Start.UseShellExecute = $false #needed to redirect console input
#$service2Start.FileName
$service2Start = Start-Process java FixedMessageSequenceClient
$service2Start.WorkingDirectory = "C:\Java_Scratch2\FixedMessageSequenceClient.java"
$service2Start.Arguments = "AsConsole"
$service2Start = [System.Diagnostics.Process]::Start($service2Start)
#$JavaServer = Start-Process java FixedMessageSequenceClient
I thought that the line
$service2Start.RedirectStandardInput = $true
would force the output to go to one CMD window.
Start both processes as background jobs and fetch the jobs' output periodically:
$server = Start-Job -Name 'fmsserver' -ScriptBlock {
Set-Location 'C:\Java_Scratch2\FixedMessageSequenceServer.java'
& java FixedMessageSequenceServer AsConsole
}
$client = Start-Job -Name 'fmsclient' -ScriptBlock {
Set-Location 'C:\Java_Scratch2\FixedMessageSequenceClient.java'
& java FixedMessageSequenceClient AsConsole
}
while ($server.State, $client.State -contains 'running') {
if ($server.HasMoreData) { Receive-Job $server }
if ($client.HasMoreData) { Receive-Job $client }
Start-Sleep -Milliseconds 100
}
# fetch remaining output after completion
Receive-Job $server
Receive-Job $client
# clean up
Remove-Job $server
Remove-Job $client