6.4. Upload and Download Files with FTP
Earlier versions of .NET didn't include any tools for FTP (File Transfer Protocol), a common protocol used to transfer files to and from a web server. As a result, you either had to purchase a third-party component or write your own (which was easy in principle but difficult to get right in practice).
Note: Need to upload files to an FTP site or download existing content? New support is available in VB 2005.
In .NET 2.0, a new FtpWebRequest class neatly fills the gap. However, the FtpWebRequest class has its own complexities, so Microsoft programmers simplified life for VB developers even further by extending the My.Computer.Network object to provide two quick access methods for completing basic FTP operations. These are UploadFile(), which sends a file to a remote server, and DownloadFile( ), which retrieves a file and stores it locally.
6.4.1. How do I do that?
Whether you use the FtpWebRequest class or the My.Computer.Network object, all FTP interaction in .NET is stateless. That means that you connect to the FTP site, perform a single operation (like transferring a file or retrieving a directory listing), and then disconnect. If you need to perform another operation, you need to reconnect. Fortunately, this process of connecting and logging in is handled automatically by the .NET Framework.
The easiest way to use FTP in a VB application is to do so through the My.Computer.Network object. If you use its FTP methods, you never need to worry about the tedious details of opening, closing, and reading streams. To download a file, the bare minimum information you need is the URL that points to the FTP site and the path that points to the local file. Here's an example:
My.Computer.Network.DownloadFile( _ "ftp://ftp.funet.fi/pub/gnu/prep/gtk.README", "c:\readme.txt")
This command retrieves the file that is on the FTP site ftp.funet.fi in the path /pub/gnu/prep/gtk.README and copies it to the local file c:\readme.txt.
Uploading uses similar parameters, but in reverse:
My.Computer.Network.UploadFile("c:\newfile.txt", _ "ftp://ftp.funet.fi/pub/newfile.txt")
This command copies the local file newfile.txt from the directory c:\ to the FTP site ftp.funet.fi, in the remote directory /pub.
Both DownloadFile( ) and UploadFile( ) support several overloads that take additional parameters, including credentials (the username and password information you might need to log on to a server) and a timeout parameter to set the maximum amount of time you'll wait for a response before giving up (the default is 1,000 milliseconds).
Unfortunately, the DownloadFile( ) and UploadFile( ) methods haven't been too robust in beta builds of Visual Basic 2005, and the methods may fail to work. An option that works better is the more sophisticated FtpWebRequest class. Not only does it perform more reliably, but it also fills a few glaring gaps in the FTP support provided by the My.Network.Computer. Because FtpWebRequest allows you to execute any FTP command, you can use it to retrieve directory listings, get file information, and more.
Note: Internet Explorer has its own built-in FTP browser. Just type a URL that points to an FTP site (like ftp://ftp.microsoft.com) into the IE address bar to browse what's there. You can use this tool to verify that your code is working correctly.
To use the FtpWebRequest class, you need to follow several steps. First, pass the URL that points to the FTP site to the shared WebRequest.Create( ) method:
Dim Request As FtpWebRequest Request = CType(WebRequest.Create("ftp://ftp.microsoft.com/MISC"), _ FtpWebRequest)
The WebRequest.Create() method examines the URL and returns the appropriate type of WebRequest object. Because FTP URLs always start with the scheme ftp://, the Create( ) method will return a new FtpWebRequest object.
Once you have the FtpWebRequest, you need to choose what FTP operation you want to perform by setting the FtpWebRequest.Method property with the text of the FTP command. Here's an example for retrieving directory information with the LIST command:
Request.Method = "LIST"
Once you've chosen the FTP operation you want to perform, the last step is to execute the command and read the response. The tricky part is the fact that the response is returned to you as a stream of text. It's up to you to move through this block of text line by line with a StreamReader and parse the information.
For example, the following code reads through a returned directory listing and displays each line in a Console window:
Dim Response As FtpWebResponse = CType(Request.GetResponse( ), FtpWebResponse) Dim ResponseStream As Stream = Response.GetResponseStream( ) Dim Reader As New StreamReader(ResponseStream, System.Text.Encoding.UTF8) Dim Line As String Do Line = Reader.ReadLine( ) Console.WriteLine(Line) Loop Until Line = ""
The output looks like this:
dr-xr-xr-x 1 owner group 0 Jul 3 2002 beckyk -r-xr-xr-x 1 owner group 15749 Apr 8 1994 CBCP.TXT dr-xr-xr-x 1 owner group 0 Jul 3 2002 csformat dr-xr-xr-x 1 owner group 0 Aug 1 2002 DAILYKB -r-xr-xr-x 1 owner group 710 Apr 12 1993 DISCLAIM.TXT dr-xr-xr-x 1 owner group 0 Jul 3 2002 FDC dr-xr-xr-x 1 owner group 0 Jul 3 2002 friKB dr-xr-xr-x 1 owner group 0 Jul 3 2002 FULLKB dr-xr-xr-x 1 owner group 0 Jul 3 2002 Homenet -r-xr-xr-x 1 owner group 97 Sep 28 1993 INDEX.TXT ...
Clearly, if you want to manipulate individual pieces of information (like the file size) or distinguish files from directories, you'll need to do extra work to parse the text returned by the StreamReader.
Finally, when you're finished with the FTP request and response, you need to close the streams:
Reader.Close( ) Response.Close( )
To put it all in context, it helps to consider a simple FTP browsing application. Figure 6-1 shows a sample application that's included with the downloadable samples for this chapter.
Figure 6-1. A simple FTP Browser application
This Windows application includes the following controls:
Example 6-4 shows code for the FTP browser form
Example 6-4. The FTP browser form
Public Class FtpForm Inherits System.Windows.Forms.Form ' Stores the path currently shown in the ListView. Private CurrentPath As String Private Sub cmdQuery_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdQuery.Click ' Check the URI is valid. Dim RequestUri As Uri = ValidateUri(txtFtpSite.Text) If RequestUri Is Nothing Then Return ' Clear the ListView. listDir.Items.Clear( ) ' Create a new FTP request using the URI. Dim Request As FtpWebRequest Request = CType(WebRequest.Create(RequestUri), FtpWebRequest) ' Use this request for getting full directory details. Request.Method = "LIST" Request.UsePassive = False Dim Response As FtpWebResponse Dim ResponseStream As Stream Dim Reader As StreamReader Try ' Execute the command and get the response. Response = CType(Request.GetResponse( ), FtpWebResponse) Debug.WriteLine("Status: " & Response.StatusDescription) ' Read the response one line at a time. ResponseStream = Response.GetResponseStream( ) Reader = New StreamReader(ResponseStream, System.Text.Encoding.UTF8) Dim Line As String Do Line = Reader.ReadLine( ) If Line <> "" Then Debug.WriteLine(Line) ' Extract just the file or directory name from the line. Dim ListItem As New ListViewItem(Line.Substring(59).Trim( )) If Line.Substring(0, 1) = "d" Then ListItem.ImageKey = "Folder" Else ListItem.ImageKey = "File" End If listDir.Items.Add(ListItem) End If Loop Until Line = "" ' Operation completed successfully. Store the current FTP path. CurrentPath = RequestUri.ToString( ) Catch Ex As Exception MessageBox.Show(Ex.Message) Finally ' Clean up. Reader.Close( ) Response.Close( ) End Try End Sub Private Sub cmdDownload_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles cmdDownload.Click ' Check the URI is valid. Dim RequestUri As Uri = ValidateUri(txtFtpSite.Text) If RequestUri Is Nothing Then Return ' Prompt the user to choose a destination folder. ' Default the file name to the same file name used on the FTP server. dlgSave.FileName = Path.GetFileName(txtFtpSite.Text) If dlgSave.ShowDialog( ) <> Windows.Forms.DialogResult.OK Then Return End If ' Create a new FTP request using the URI. Dim Request As FtpWebRequest Request = CType(WebRequest.Create(RequestUri), FtpWebRequest) ' Use this request for downloading the file. Request.UsePassive = False Request.Method = "RETR" Dim Response As FtpWebResponse Dim ResponseStream, TargetStream As Stream Dim Reader As StreamReader Dim Writer As StreamWriter Try ' Execute the command and get the response. Response = CType(Request.GetResponse( ), FtpWebResponse) Debug.WriteLine("Status: " & Response.StatusDescription) Debug.WriteLine("File Size: " & Response.ContentLength) ' Create the destination file. TargetStream = New FileStream(dlgSave.FileName, FileMode.Create) Writer = New StreamWriter(TargetStream) ' Write the response to the file. ResponseStream = Response.GetResponseStream( ) Reader = New StreamReader(ResponseStream, System.Text.Encoding.UTF8) Writer.Write(Reader.ReadToEnd( )) Catch Err As Exception MessageBox.Show(Err.Message) Finally ' Clean up. Reader.Close( ) Response.Close( ) Writer.Close( ) End Try End If End Sub Private Function ValidateUri(ByVal uriText As String) As Uri Dim RequestUri As Uri Try ' Check that the string is interpretable as a URI. RequestUri = New Uri(uriText) ' Check that the URI starts with "ftp://" If RequestUri.Scheme <> Uri.UriSchemeFtp Then RequestUri = Nothing End If Catch RequestUri = Nothing End Try If RequestUri Is Nothing Then MessageBox.Show("Invalid Uri.") Else End If Return RequestUri End Function Private Sub listDir_SelectedIndexChanged(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles listDir.SelectedIndexChanged ' When a new item is selected in the list, add this ' to the URI in the text box. If listDir.SelectedItems.Count <> 0 Then CurrentPath = CurrentPath.TrimEnd("/") txtFtpSite.Text = CurrentPath & "/" & listDir.SelectedItems(0).Text End If End Sub End Class
The most complex code found in this example occurs in the event handler for the cmdQuery button, which retrieves a directory listing, parses out the important information, and updates the ListView.
6.4.2. Where can I learn more?
In previous builds, the MSDN help included much more information on FTP access and different FTP operations under the index entry "FtpMethods," complete with useful demonstrations of the different methods. This entry has disappeared in recent builds (along with the FtpMethods class), but check for it to return. In the meantime, you can read up on the FTP protocol and supported commands at www.vbip.com/winsock/winsock_ftp_ref_01.asp.