FTP Plain Text File To Specific Directory - vb.net

I am taking over a process that is completely done by hand and trying to automate it. One step is to FTP a plain text file to a specific directory on an FTP server. Everything I see when I search is dropping the file on the server not putting it in a specific directory. The error I get is file name not allowed, except it is the same name that is manually FTP'ed. Below is what code I have. What am I missing?
LocalFile = Path.GetFileName(TheFile)
URI = TheHost & RemoteDir & LocalFile
TheFtpClient = CType(FtpWebRequest.Create(URI), FtpWebRequest)
TheFtpClient.Credentials = New NetworkCredential(UserName, PassWord)
TheFtpClient.KeepAlive = False
TheFtpClient.Method = WebRequestMethods.Ftp.UploadFile
FileReader = New StreamReader(TheFile)
FileContents = Encoding.UTF8.GetBytes(FileReader.ReadToEnd())
FileReader.Close()
TheFtpClient.ContentLength = FileContents.Length
RequestStream = TheFtpClient.GetRequestStream()
RequestStream.Write(FileContents, 0, FileContents.Length)
RequestStream.Close()
TheResponse = CType(TheFtpClient.GetResponse(), FtpWebResponse)
Console.WriteLine("[" & TheResponse.StatusDescription & "]")

Related

VB.NET to download latest file name with timestamp in its name from FTP server

I want to pull a file from my FTP server into my local drive. However the most difficult part of do this is that the file name is a timestamp that changes on a daily basis. The program should download the file regardless of how it has changed with the date; year, month, hour, minutes, and seconds. The format of the name are always the same. File name example is below in bold. Please advise!
For example,
username - meUser
password - mepswrd
and the URL to the FTP
/afea/euser/aefe/aole/efa/
and the path I want the file to save after download it.
C:\Users\alae\Desktop\loaef
and the file is in FORMAT
20160223.171234.BA_DESRP_20160121.txt
The only part to the file that doesn't change is BA_DESRP, all the other part can change as it is a timestamp.
This is my code to start with:
Const lf As String = "C:\Users\alae\Desktop\loaef"
Const rf As String = "/afea/euser/aefe/aole/efa/"
Const ht As String = "host"
Const un As String = "username"
Const pw As String = "password"
Dim URI As String = ht & rf
Dim ftp As System.Net.FtpWebRequest = _
CType(FtpWebRequest.Create(URI), FtpWebRequest)
ftp.Credentials = New _
System.Net.NetworkCredential(un, pw)
There's no easy way with the FtpWebRequest (or any other functionality readily available in the .NET framework). You have to:
List the remote directory using the WebRequestMethods.Ftp.ListDirectory
Filter the returned list to those containing the BA_DESRP
Select the latest out of those
And download it
Dim url As String = "ftp://ftp.example.com/remote/path/"
Dim credentials As NetworkCredential = New NetworkCredential("username", "password")
Const localPath = "C:\local\path"
Dim listRequest As FtpWebRequest = WebRequest.Create(url)
listRequest.Method = WebRequestMethods.Ftp.ListDirectory
listRequest.Credentials = credentials
Dim latest As String = Nothing
Using listResponse As FtpWebResponse = listRequest.GetResponse(),
listStream As Stream = listResponse.GetResponseStream(),
listReader As StreamReader = New StreamReader(listStream)
While Not listReader.EndOfStream
Dim filename As String = listReader.ReadLine()
If filename.Contains("BA_DESRP") Then
Console.WriteLine("Found {0} ...", filename)
If (latest Is Nothing) OrElse (latest < filename) Then
latest = filename
End If
End If
End While
End Using
If Not latest Is Nothing Then
Console.WriteLine("Downloading {0} ...", latest)
Dim webClient As New WebClient()
webClient.Credentials = credentials
webClient.DownloadFile(url + latest, Path.Combine(localPath, latest))
End If
Or use another FTP library with more powerful functionality.
For example with WinSCP .NET assembly:
Const localPath = "C:\local\path\"
Const remotePath = "/remote/path"
Dim sessionOptions As New SessionOptions
With sessionOptions
.Protocol = Protocol.Ftp
.HostName = "ftp.example"
.UserName = "username"
.Password = "password"
End With
Using session As New Session
session.Open(sessionOptions)
Dim latest As RemoteFileInfo =
session.ListDirectory(remotePath).Files.
Where(Function(file) file.Name.Contains("BA_DESRP")).
OrderByDescending(Function(file) file.Name).
FirstOrDefault()
If Not latest Is Nothing Then
Console.WriteLine("Downloading {0} ...", latest)
session.GetFiles(latest.FullName, localPath).Check()
End If
End Using
If you can use the actual file timestamp (not the timestamp in its name), it would be even more straightforward. See Downloading the most recent file.
(I'm the author of WinSCP)
This will get you the list of files in the directory. Since your file name already includes the timestamp, you just need to parse it out from the file name, and then ultimately store each timestamp in a variable where you can then compare the next file's time stamp to. if next file is greater than the value you in your variable, assign that value. finally when you are done, your var should reflect the file name you need and at that stage you can call the download method to get the file.
Dim request As FtpWebRequest = WebRequest.Create("ftp://" & "servername" & "/" & "directory" & "/*")
request.Method = WebRequestMethods.Ftp.ListDirectory
request.Credentials = New NetworkCredential("username", "password")
Using reader As New StreamReader(request.GetResponse().GetResponseStream())
Do Until reader.EndOfStream
Console.WriteLine(reader.ReadLine())
Loop
End Using
since you say thousands for files, this route may not be the best option for you. if you are not restricted to .NET, you may want to explore alternative options, such as using a ftp client where you may be able to search by the actual time stamp on files...

Delete FTP files with name containing a string (matching mask)

I would like to delete all files on an FTP server, whose names contain a certain string.
For example I have these files on the FTP:
pippo_1.jpg
pippo_2.jpg
pippo_3.jpg
pluto_1.jpg
I would like delete all file that contain pippo.
How can I delete all these files with a single operation?
Thank you!
No implementation of the FTP protocol in .NET framework allows this in a single operation.
You have to list the remote directory, filter the files you want to delete and delete them one by one.
If you really want to do this in a single operation, you have use a 3rd party library, that supports operations with file masks. For example WinSCP .NET assembly allows this with its Session.RemoveFiles method:
Dim sessionOptions As New SessionOptions
With sessionOptions
.Protocol = Protocol.Ftp
.HostName = "ftp.example.com"
.UserName = "username"
.Password = "password"
End With
Using session As New Session
session.Open(sessionOptions)
session.RemoveFiles("/remote/path/pippo*").Check()
End Using
(I'm the author of WinSCP)
If you do not want to use a 3rd party library, do as suggested above:
Dim url As String = "ftp://ftp.example.com/remote/path/"
Dim credentials As NetworkCredential = New NetworkCredential("username", "password")
Dim listRequest As FtpWebRequest = WebRequest.Create(url)
listRequest.Method = WebRequestMethods.Ftp.ListDirectory
listRequest.Credentials = credentials
Using listResponse As FtpWebResponse = listRequest.GetResponse(),
listStream As Stream = listResponse.GetResponseStream(),
listReader As StreamReader = New StreamReader(listStream)
While Not listReader.EndOfStream
Dim filename As String = listReader.ReadLine()
If filename.StartsWith("pippo") Then
Dim deleteRequest As FtpWebRequest = WebRequest.Create(url + filename)
deleteRequest.Method = WebRequestMethods.Ftp.DeleteFile
deleteRequest.Credentials = credentials
deleteRequest.GetResponse()
End If
End While
End Using

Check for file/subdirectory existence with VB.NET using FtpWebRequest

I have a problem with my FTP upload and I hope you can help me. I'm trying create folders and then upload files to them. What my program should do is checking if a folder already exists, and if not, create a new one with the name checked before. The program runs fine, except for the error described below.
My Problem:
I want to upload a folder called ghandle -> works as intended.
After that, I want to upload a folder called handle -> doesn't work, because the .Contains method that checks the folders on the FTP server, finds ghandle and stops, because ghandle contains handle.
Are there other options like .Contains which will just check for whole words or exact matches?
Here is my source code:
Dim dirname = Path.GetFileNameWithoutExtension(openFileDialogHtml.FileName) & "_files"
Dim ftp = "ftp://" & ftp_address.Text & "/"
Dim user = ftp_user.Text
Dim pass = ftp_password.Text
Dim request As Net.FtpWebRequest = Net.FtpWebRequest.Create(ftp)
Dim creds As Net.NetworkCredential = New Net.NetworkCredential(user, pass)
request.Credentials = creds
Dim resp As Net.FtpWebResponse = Nothing
request.Method = Net.WebRequestMethods.Ftp.ListDirectoryDetails
request.KeepAlive = True
Using resp
resp = request.GetResponse()
Dim sr As StreamReader = New StreamReader(resp.GetResponseStream(), System.Text.Encoding.ASCII)
Dim s As String = sr.ReadToEnd()
If Not s.Contains(dirname) Then
request = Net.FtpWebRequest.Create(ftp & dirname)
request.Credentials = creds
request.Method = Net.WebRequestMethods.Ftp.MakeDirectory
resp = request.GetResponse()
MsgBox("Created folder " & dirname)
Else
MsgBox("Folder " & dirname & " already exists!")
End If
End Using
Thanks in advance
First, use ListDirectory, not ListDirectoryDetails. The ListDirectory returns plain names only, what is enough for your purpose and easy to parse.
Then just split the output to an array of individual file names, using the String.Split method:
Dim names As String() =
sr.ReadToEnd().Split(
New Char() {vbCr, vbLf}, StringSplitOptions.RemoveEmptyEntries)
And use the IEnumerable.Contains extension method to check for given file name:
If Not names.Contains(dirname) Then

Uploading data from MemoryStream with FtpWebRequest seems to send the same data each time

This has taken up most of my afternoon!
The following code is used to upload, via FTP from either a local source (in which case it copies) or from a MemoryStream object.
Sub UploadViaFtp(DestinationFilePath As String, Optional LocalSource As String = "", Optional mStream As MemoryStream = Nothing)
Dim ftpfullpath As String = HostName.TrimEnd("/") & "/" & DestinationFilePath.Trim("/")
Dim Directory As String = ThinkkaCommon.SplitStrOnLast(DestinationFilePath, "/")(0)
Call MakeDir(HostName, Directory) ' Make sure the FTP Location Exists
Try
Dim ftp As FtpWebRequest = DirectCast(FtpWebRequest.Create(ftpfullpath), FtpWebRequest)
ftp.Credentials = New NetworkCredential(FtpUser, FtpPwd)
ftp.KeepAlive = False
ftp.UseBinary = True
ftp.Method = WebRequestMethods.Ftp.UploadFile
If mStream Is Nothing Then
' If we are copying from a local file
Dim buffer As Byte() = File.ReadAllBytes(LocalSource)
ftp.ContentLength = buffer.Length
Using reqStream As Stream = ftp.GetRequestStream()
reqStream.Write(buffer, 0, buffer.Length)
End Using
ElseIf mStream IsNot Nothing Then
' Writing to FTP From a Stream
Using mStream
Using stOut As Stream = ftp.GetRequestStream()
stOut.Write(mStream.GetBuffer(), 0, CInt(mStream.Length))
ftp.Abort() ' << Trying to clear
End Using
End Using
End If
ftp = Nothing ' << Trying to clear
Catch ex As Exception
' Handle error
End Try
End Sub
For local files it is working absolutely perfectly.
However, I am trying to create some text files (e.g. robot.txt) programmatically and upload directly from a MemoryStream to the FTP.
I have this function to create the MemoryStream object:
Private Function WriteRobots(block_robots As Boolean) As MemoryStream
Dim rStream As New MemoryStream()
Dim rStringWriter As New StreamWriter(rStream, Encoding.UTF8)
Dim RobotString As String = ""
If block_robots Then
RobotString = "User-agent: *" & vbNewLine & _
"Disallow: /" & vbNewLine
Else
RobotString = "User-agent: *" & vbNewLine & _
" Disallow: /cgi-bin/" & vbNewLine & _
" Disallow: /Layouts/" & vbNewLine & _
" Disallow: /Bin/" & vbNewLine & _
" Disallow: /MyAccount/" & vbNewLine & _
" Disallow: /LoginForm/" & vbNewLine
End If
rStringWriter.Write(RobotString)
rStringWriter.Flush()
rStream.Position = 0
Return rStream
End Function
Using the above two methods I then call this:
UploadViaFtp("/robots.txt", mStream:=WriteRobots(True))
This should create a file with just this text:
User-agent: *
Disallow: /
Which it should save to robots.txt.
And it does... BUT...
If I change my block_robots parameter to False and then call the same code, I should get a different robots.txt file, but I get the exact same file every time.
I have tried changing the code at carious stages, completely changing the content and so on, but, the only way I seem to be able to get the new file to work is by changing the filename:
UploadViaFtp("/robots.txt", mStream:=WriteRobots(False)) '< Creates the same file despite the `False` value
UploadViaFtp("/Another_File_Name.txt", mStream:=WriteRobots(False)) ' < Creates the correct file
You can see I have added ftp.Abort and ftp=Nothing into the code to try to clear the FTP stream, but it is not making any difference.
It seems as if the FTP stream is remaining open and re-posting the same data even after I have changed the parameter.
Note; the True/False parameter is changed via a form elsewhere in my application and I have verified that this is changing correctly, I am not being stupid there, the function is definitely being called with True/False value being changed appropriately
Your code is correct. It works for me.
Of course, except for the ftp.Abort(), which is non-sense as you abort the FTP session even before the file is finished uploading.
Make sure you handle the exceptions. Currently you ignore all exceptions, so won't even know, if something goes wrong.
If this does not help, enable .NET network logging and show us the log.

Download file from ftp if newer or different

I am trying to download a file from an ftp site only if it's newer than my local file. Can anyone help how to incorporate to check for the file properties? right now it downloads the file, but just need if newer. The purpose is to update a .mdb with the contents of the file, so don't want to download file and run an update everytime my app runs, only if the file is different. This is the code I am using:
Const localFile As String = "C:\version.xml"
Const remoteFile As String = "/version.xml"
Const host As String = "ftp://1.1.1.1"
Const username As String = "user"
Const password As String = "pwd"
Dim URI As String = host & remoteFile
Dim ftp As System.Net.FtpWebRequest = _
CType(FtpWebRequest.Create(URI), FtpWebRequest)
ftp.Credentials = New _
System.Net.NetworkCredential(username, password)
ftp.KeepAlive = False
ftp.UseBinary = True
ftp.Method = System.Net.WebRequestMethods.Ftp.DownloadFile
Using response As System.Net.FtpWebResponse = _
CType(ftp.GetResponse, System.Net.FtpWebResponse)
Using responseStream As IO.Stream = response.GetResponseStream
Using fs As New IO.FileStream(localFile, IO.FileMode.Create)
Dim buffer(2047) As Byte
Dim read As Integer = 0
Do
read = responseStream.Read(buffer, 0, buffer.Length)
fs.Write(buffer, 0, read)
Loop Until read = 0
responseStream.Close()
fs.Flush()
fs.Close()
End Using
responseStream.Close()
End Using
response.Close()
End Using
Any help is appreciated
Not sure if this answers your question but I am looking for a similar answer and came across this.
http://msdn.microsoft.com/en-us/library/system.io.fileinfo.aspx
Look into LastWriteTime and you could save that time and check to see if it is a newer date then what is saved. You would have to also figure out how to download the file as a while(not familiar with the code maybe you are).