vb.net FTP - works in some cases - vb.net

i currently have this code as my FTP code (Which works a charm!)
Using ms As New System.IO.MemoryStream
test.Image.Save(ms, System.Drawing.Imaging.ImageFormat.Png)
Using wc As New System.Net.WebClient
wc.UploadData("ftp://" & My.Settings.username & ":" & My.Settings.password & "#ftp." & My.Settings.server & My.Settings.imagelocation & filename & ".jpg", ms.ToArray())
End Using
End Using
however i have a new username and directory etc, and they contain "#" this is my username for example: "snap#example.com"
but my username before would be for instance "test" you see without the "#" im guessing its because it messes with the "#ftp" part, Any suggestions how to fix?

Short answer:
Encode your username and password using System.Web.HttpUtility.UrlPathEncode before creating your URL.
Long answer:
According to the IETF's RFC 1738, which is a standards track document about URLs, it explicitly says
Within the user and password field, any ":", "#", or "/" must be encoded.
The actual URL spec document does not explicitly mention encoding special characters in usernames and passwords, but I think it's implied that you can encode them. And it says
Because a % sign always indicates
an encoded character, a URL may be made safer simply by encoding
any characters considered unsafe, while leaving already encoded.
So you should percent-escape any special charaters in your URL, and in the case of an '#' that's %40.

Alternatively you could use the following classes provided by .net:
FtpWebRequest
WebRequestMethods
NetworkCredential
Private Shared Sub UploadFileToFTP(source As String)
Try
Dim filename As String = Path.GetFileName(source)
Dim ftpfullpath As String = ftpurl
Dim ftp As FtpWebRequest = DirectCast(FtpWebRequest.Create(ftpfullpath), FtpWebRequest)
ftp.Credentials = New NetworkCredential(ftpusername, ftppassword)
ftp.KeepAlive = True
ftp.UseBinary = True
ftp.Method = WebRequestMethods.Ftp.UploadFile
Dim fs As FileStream = File.OpenRead(source)
Dim buffer As Byte() = New Byte(fs.Length - 1) {}
fs.Read(buffer, 0, buffer.Length)
fs.Close()
Dim ftpstream As Stream = ftp.GetRequestStream()
ftpstream.Write(buffer, 0, buffer.Length)
ftpstream.Close()
Catch ex As Exception
Throw ex
End Try
End Sub

Related

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

Checking the length of a stream

So i have written code to allow me to download files from an FTP server using the system.io.stream function in vb.net. Now I need to be able to find the length of the stream, when i has finished.
I have worked with streamreader in the past but this stream doesn't work in the same way so i'm not sure how to proceed.
Try
Dim request As System.Net.FtpWebRequest
request = DirectCast(System.Net.WebRequest.Create("ftp://ftp.server.com/Folder/" & FileName & ".pdf"), System.Net.FtpWebRequest)
request.Credentials = New System.Net.NetworkCredential("Username", "Password")
request.Method = System.Net.WebRequestMethods.Ftp.DownloadFile
Dim stream As System.IO.Stream = request.GetResponse.GetResponseStream
'Dim OutPutFilepath As String = "DownloadTest" & "\" & IO.Path.GetFileName("ftp://ftp.Server.com/Folder/")
output = System.IO.File.Create("C:\Users\ASUS\Documents\TestFile2.pdf")
output.Close()
stream.Close()
Console.WriteLine("Downloaded")
Console.ReadLine()
Catch ex As Exception
MsgBox(ex.Message)
End Try
Also, I have tried using the stream.length feature however, i got an exception saying something along the lines of "this stream does not support seek functions".
Thanks in advance

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.

Writing FileStream to Local Path and not the server

I have the following code working successfully:
Protected Sub ExportExcel_Click(sender As Object, e As EventArgs) Handles ExportExcel.Click
Dim warnings As Warning()
Dim streamids As String()
Dim mimeType As String
Dim encoding As String
Dim filenameExtension As String
Dim fileName As String = "D:\Report" & DateTime.Now.ToString("yyyyMMdd_HHmmss") & ".xls"
Dim bytes As Byte() = ReportViewer1.LocalReport.Render("Excel", Nothing, mimeType, encoding, filenameExtension, streamids, warnings)
Using fs As New FileStream(fileName, FileMode.Create)
fs.Write(bytes, 0, bytes.Length)
End Using
lblMessage.Text = Functions.GetMessageConfirm("Report downloaded successfully in your D:/ at: " & Now.ToString)
End Sub
This code saves the file in the web server. I want to save the file on the client machine.
You're halfway there, probably. You can't just save files on the client anyway. Clients are webbrowsers, and they run JavaScript.
What you can do is use the download functionality of webbrowsers, to let them download the file which you just created. To do so, put the output on the server in a directory from where it can be downloaded, then return the new URL to the client.

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).