Search content within the blog

Wednesday, January 5, 2011

Building a Simple FTP Application Using C# 2.0

Introduction
An addition to the Microsoft .NET Framework 2.0 to 1.x is the support for FTP. All these days we had to rely on 3rd party libraries which suited most of our needs, but for sure, there is an extra pleasure using the .net framework library classes. The code included is not designed to be a full-fledged reusable library, but rather an easy to use and reusable pieces of code which is easily comprehensible and can be reused and tweaked to fit your specific needs. Therefore, the code for each functionality (upload, download, delete etc…) can be easy picked up separately and reused. The main motive behind this article was the unavailability of .NET 2.0 FTP sample codes and their usage in C#; may be because it is a new entrant to the .NET scenario or the third party implementations available were working pretty well, but this area of the .NET 2.0 library has not gotten enough focus.

<span style="font-weight:bold;">Background</span>
I started working on this FTP module as part of my official work, but the requirement soon changed and I had to do it for .NET 1.1. So, I have not traveled deeper into the rabbit hole. But I believe this gives a good, instant start for using the FTP support in .NET 2.0.

Listing 1 - Directives
First, you should add the following directives:
using System.Net;
using System.IO;

The following steps can be considered as a generic procedure of getting an FTP request executed using FtpWebRequest object.

1. Create an FtpWebRequest object over an ftp server URI.

2. Set the FTP method to execute (upload, download, etc.).

3. Set the options (SSL support, transfer as binary/not etc.) for the FTP webrequest.

4. Set the login credentials (username, password).

5. Execute the request.

6. Receive the response stream (if required).

7. Close the FTP Request, in addition to any open streams.

One point to watch out while coding for any FTP application is to have the settings for the ftp request proper to suit the ftp server and its specific configurations. FtpWebRequest object exposes many properties to have these settings in place.

The sample for the upload functionality is as follows:

First a URI is created which represents the FTP address along with the filename (directory structure included). This URI is used to create the FtpWebRequest instance.
Then properties of the FtpWebRequest object are set, which determines the settings for the ftp request. Some of its important properties are:

Credentials - Specifies the username and password to login to the FTP server.

KeepAlive - Specifies if the control connection should be closed or not after the request is completed. By default it is set to true.

UseBinary - Denotes the datatype for file transfers. The 2 modes of file transfer in this case are Binary and ASCII. At bit level both vary in the 8th bit of a byte. ASCII uses 8th bit as insignificant bit for error control, where as, for binary all the 8 bits are significant. So take care when you go for the ASCII transmission. To be simple, all those files that open and read well in notepad are safe as ASCII. Executables, formatted docs, etc. should be sent using binary mode. BTW sending ASCII files as binary works fine most of the time.

UsePassive - Specifies to use either active or passive mode. Earlier active FTP worked fine with all clients, but now a days as most of the random ports will blocked by firewall and the active mode may fail. The passive FTP is helpful in this case, but still it causes issues at the server. The higher ports requested by client on server may also be blocked by firewall. But since FTP servers will need to make their servers accessible to the greatest number of clients, they will almost certainly need to support passive FTP. The reason why passive mode is considered safe is that it ensures all data flow initiation comes from inside (client) the network rather than from the outside (server).

Contentlength - Setting this property is useful for the server we request to, but is not of much use for us (client) because FtpWebRequest usually ignores this property value, so it will not be available for our use in most of the cases. If we set this property, the FTP server will get an idea in advance about the size of the file it should expect (in case of upload).

Method - Denotes what action (command) to take in the current request (upload, download, filelist, etc.). It is set a value defined in the WebRequestMethods.Ftp structure.

Listing 2 - Upload

private void Upload(string filename)
{
FileInfo fileInf = new FileInfo(filename);
string uri = "ftp://" +
ftpServerIP + "/" + fileInf.Name;
FtpWebRequest reqFTP;

// Create FtpWebRequest object from the Uri provided
reqFTP =
(FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + ftpServerIP +
"/" + fileInf.Name));

// Provide the WebPermission Credintials
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);

// By default KeepAlive is true, where the control connection is not closed
// after a command is executed.
reqFTP.KeepAlive = false;

// Specify the command to be executed.
reqFTP.Method = WebRequestMethods.Ftp.UploadFile;

// Specify the data transfer type.
reqFTP.UseBinary = true;

// Notify the server about the size of the uploaded file
reqFTP.ContentLength = fileInf.Length;

// The buffer size is set to 2kb
int buffLength = 2048;
byte[] buff = new byte[buffLength];
int contentLen;

// Opens a file stream (System.IO.FileStream) to read the file to be uploaded
FileStream fs = fileInf.OpenRead();

try
{
// Stream to which the file to be upload is written
Stream strm = reqFTP.GetRequestStream();

// Read from the file stream 2kb at a time
contentLen = fs.Read(buff, 0, buffLength);

// Till Stream content ends
while (contentLen != 0)
{
// Write Content from the file stream to the FTP Upload Stream
strm.Write(buff, 0, contentLen);
contentLen = fs.Read(buff, 0, buffLength);
}

// Close the file stream and the Request Stream
strm.Close();
fs.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "Upload Error");
}
}

Above is a sample code for FTP Upload (PUT). The underlying sub command used is STOR. Here an FtpWebRequest object is made for the specified file on the ftp server.
Different properties are set for the request, namely Credentials, KeepAlive Method, UseBinary, ContentLength.

The file in our local machine is opened and the contents are written to the FTP request stream. Here a buffer of size 2kb is used as an appropriate size suited for upload of larger or smaller files.

Listing 3 - Download
private void Download(string filePath, string fileName)
{
FtpWebRequest reqFTP;
try
{
//filePath = <<The full path where the file is to be created. the>>,
//fileName = <<Name of the file to be createdNeed not name on FTP server. name name()>>
FileStream outputStream = new FileStream(filePath + "\\" + fileName, FileMode.Create);

reqFTP = (FtpWebRequest)FtpWebRequest.Create(new
Uri("ftp://" + ftpServerIP + "/" + fileName));
reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
Stream ftpStream = response.GetResponseStream();
long cl = response.ContentLength;
int bufferSize = 2048;
int readCount;
byte[] buffer = new byte[bufferSize];

readCount = ftpStream.Read(buffer, 0, bufferSize);
while (readCount > 0)
{
outputStream.Write(buffer, 0, readCount);
readCount = ftpStream.Read(buffer, 0, bufferSize);
}

ftpStream.Close();
outputStream.Close();
response.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}

Above is a sample code for Download of file from the FTP server. Unlike the Upload functionality described above, Download would require the response stream, which will contain the content of the file requested.

Here the file to download is specified as part of the Uri which in turn is used for the creation of the FtpWebRequest object. To "GET" the file requested, get the response of the FtpWebRequest object using GetResponse() method. This new response object built provides the response stream, which contain the file content as stream that you can easily convert to a file stream to get the file in place.

Note: We have the flexibility to set the location and name of the file under which it is to be saved on our local machine.

Listing 4 – Retrieve Filelist on FTP Server

public string[] GetFileList()
{
string[] downloadFiles;
StringBuilder result = new StringBuilder();
FtpWebRequest reqFTP;
try
{
reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + ftpServerIP + "/"));
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential(ftpUserID, ftpPassword);
reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
WebResponse response = reqFTP.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());

string line = reader.ReadLine();
while (line != null)
{
result.Append(line);
result.Append("\n");
line = reader.ReadLine();
}
// to remove the trailing '\n'
result.Remove(result.ToString().LastIndexOf('\n'), 1);
reader.Close();
response.Close();
return result.ToString().Split('\n');
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show(ex.Message);
downloadFiles = null;
return downloadFiles;
}
}

Above is a sample block of code for getting the file list on the ftp server. The Uri is built specifying the FTP server address/name and the required path if any. In the above example the root folder is specified for the creation of the FtpWebRequest object. Here the response stream is used for the creation of a StreamReader object, which has the whole list of file names on the server separated by "\r\n." this is the newline and carriagereturn together. You can get the whole file list ("\r\n" separated) using the ReadToEnd() method of the StreamReader object. The above implementation reads each file name and creates a StringBuilder object by appending each file name. The resultant StringBuilder object is split into a string array and returned. I am sure there are better ways to do it like removing the whole '\r" instances from the whole list (returned by <<StreamReader>>.ReadToEnd())) and split the resultant string uisng "\n" delimiter. Anyway, I did not want to spend more of my energy and time pondering over it.

The implementations for Rename, Delete, GetFileSize, FileListDetails, MakeDir are very similar to the above pieces of code and the attached code is easily comprehensible.

Note: For Renaming, the new name can be assigned to the RenameTo property of FtpWebRequest object. For MakeDirectory, the name of the new directory can be specified as part of the Uri used to create FtpWebRequest object.


Please take note of the following points while coding in this area


Unless the EnableSsl property is true, all data and commands, including your user name and password information, are sent to the server in clear text. Anyone monitoring network traffic can view your credentials and use them to connect to the server. If you are connecting to an FTP server that requires credentials and supports Secure Sockets Layer (SSL), you should set EnableSsl to true.

If you do not have the proper WebPermission to access the FTP resource, a SecurityException exception is thrown.

Requests are sent to the server by calling the GetResponse method. When the requested operation completes, an FtpWebResponse object is returned. The FtpWebResponse object provides the status of the operation and any data downloaded from the server. That is,
StatusCode property of FtpWebResponse object provides the latest status code returned by the FTP server. StatusDescription property of FtpWebResponse object provides the description of the status code returned.

<span style="font-weight:bold;">Code and original reference:</span> http://aspalliance.com/1187_Building_a_Simple_FTP_Application_Using_C_20.all

Cookies - background color selector

Cookies are small pieces of text, stored on the client's computer to be used only by the website setting the cookies. This allows webapplications to save information for the user, and then re-use it on each page if needed. Here is an example where we save a users choice of background color:

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Cookies</title>
</head>
<body runat="server" id="BodyTag">
<form id="form1" runat="server">
<asp:DropDownList runat="server" id="ColorSelector" autopostback="true" onselectedindexchanged="ColorSelector_IndexChanged">
<asp:ListItem value="White" selected="True">Select color...</asp:ListItem>
<asp:ListItem value="Red">Red</asp:ListItem>
<asp:ListItem value="Green">Green</asp:ListItem>
<asp:ListItem value="Blue">Blue</asp:ListItem>
</asp:DropDownList>
</form>
</body>
</html>


The page simply contains a DropDownList control, which automatically posts back each time a new item is selected. It has 3 different colors, besides the default one, which is simply white. Once a new item is selected, the ColorSelector_IndexChanged method is fired, from our CodeBehind file:

using System;
using System.Data;
using System.Web;

public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if(Request.Cookies["BackgroundColor"] != null)
{
ColorSelector.SelectedValue = Request.Cookies["BackgroundColor"].Value;
BodyTag.Style["background-color"] = ColorSelector.SelectedValue;
}
}

protected void ColorSelector_IndexChanged(object sender, EventArgs e)
{
BodyTag.Style["background-color"] = ColorSelector.SelectedValue;
HttpCookie cookie = new HttpCookie("BackgroundColor");
cookie.Value = ColorSelector.SelectedValue;
cookie.Expires = DateTime.Now.AddHours(1);
Response.SetCookie(cookie);
}
}


Okay, two different parts to be explained here. First, the Page_Load method, which is called on each page request. Here we check for a cookie to tell us which background color should be used. If we find it, we set the value of our dropdown list to reflect this, as well as the background color of the page, simply by accessing the style attribute of the body tag.

Then we have the ColorSelector_IndexChanged method, which is called each time the user selects a new color. Here we set the background color of the page, and then we create a cookie, to hold the value for us. We allow it to expire after one hour, and then we set it by calling the SetCookie method on the Response object.

Try running the example, and set a color. Now close the browser, and start it up again. You will see that the choice of color is saved, and it will remain saved for an hour. However, nothing prevents you from saving the choice for much longer. Simply add a bigger value to the expiry date, or set an absolute value for it.

Tuesday, January 4, 2011

Page.IsValid and Page.Validate methods

ASP.net ships with a couple of validator controls that allow you to determine whether the value of the input controls they are validating is valid.
Here is a simple example of a TextBox control with a RequiredFieldValidator attached and a Button control.

<asp:TextBox ID="TextBox1" runat="server" ValidationGroup="MyValidationGroup"></asp:TextBox>

<asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="TextBox1"
ErrorMessage="This field is required!" ValidationGroup="MyValidationGroup"></asp:RequiredFieldValidator>
<br />

<asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" ValidationGroup="MyValidationGroup" />


Note that all controls belong to the same ValidationGroup - a new feature of ASP.net 2.0. With JavaScript turned on, when a user clicks the button, they will see an error message displayed next to the control being validated if they fail to fill in the TextBox. (One could also use a ValidationSummary control)

With JavaScript turned off, what may not be known is that, on the server side, even though the validators fire, it is left to the developer on how to use that information.

You may think you have built a secure application but a hacker could disable JavaScript and bypass *all* your validators! This is where the Page.Validate method and more importantly, the Page.IsValid property come in.

The Validate method is fired automatically by controls that have the CausesValidation property set to true. Note that the Button control's CausesValidation property is true by default. The Validate method iterates through all enabled validation controls on the page and validates them. This event occurs after the Load event in the page lifecycle.

If the control that raised the event has a ValidationGroup specified, then only enabled validator controls that belong to the same ValidationGroup are validated by calling the Page.Validate(ValidationGroup) overload method. As mentioned previously, this is done automatically for controls that have the CausesValidation property set to true.

The Page.IsValid property tells you whether the validation succeeded or not. It can be called only after the Page.Validiate method is called. By using this property, you can add logic to your page to determine whether to proceed with the PostBack event or not. So, in addition to relying on client side validation, it is also important that you call Page.IsValid when handling the postback event. Here is what the code behind will look like:


protected void Button1_Click(object sender, EventArgs e) {



//Not required since the CausesValidation property of the Button control is true by default.

//Page.Validate("MyValidationGroup");



//Proceed only if the vaidation is successfull

if (!Page.IsValid) {

return;

}



//Insert data into SQL



}