Managing FTP Transfers from an ASP.NET Web Page

By John Peterson

I was recently working on a project in which one of the requirements was that certain query results be sent to a different company via FTP. Granted things would have been much easier if the receiving company had simply set up a web service, but that didn't seem to be an option. They had their little FTP system already set up and had no interest in changing anything.

While I'm no stranger to FTP, I hadn't needed to perform an FTP operation from a web page in a very long time so I was curious how well .NET handled FTP. I did a little searching and came up with some code snippets that worked, but they all seemed unnecessarily complex. So I set out to see if I could make things a little simpler.

Deleting a File

In the project I was working on, the only actual requirement was to upload a file. However, since I knew that would be a little more complex, I decided to start with something simpler. The simplest FTP command I could think of was deleting a file. The reason it's so simple is because aside from providing the file name and telling the server you want to delete it, there's no real data to transfer. By starting with something simple like this you can get the basics working and smooth out some of the kinks before you move on to the more complex aspects involved in transferring data back and forth across the wire.

Here's the basic code involved in deleting a file on a remote FTP server.

    Protected Sub btnDeleteFile_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim myFtpWebRequest As FtpWebRequest
        Dim myFtpWebResponse As FtpWebResponse

        myFtpWebRequest = WebRequest.Create("ftp://ftp_server_name/filename.ext")

        'myFtpWebRequest.Credentials = New NetworkCredential("username", "password")

        myFtpWebRequest.Method = WebRequestMethods.Ftp.DeleteFile

        myFtpWebResponse = myFtpWebRequest.GetResponse()

        litResponse.Text = myFtpWebResponse.StatusDescription

        myFtpWebResponse.Close()
    End Sub

It's actually very similar to making an HTTP request. The way .NET handles it, each request is split into two parts: the request and the response. The request is the query you send to the FTP server and the response is the answer it sends back. In this case, our request is represented by the FtpWebRequest object and simply includes the name of the file we want to delete and the fact that we want to delete it. Once we provide that information, we call the GetResponse method of our FtpWebRequest object. The GetResponse method issues the request to the remote server and returns a handle to the reply as an FtpWebResponse object. Since there's really no data to be returned, all that's left for us to do is take a look at the StatusCode or StatusDescription property in order to see if the FTP server was able to our fulfill our request or not.

I did all my testing on an FTP server which accepted anonymous connections, but I've included a comment in the listing above that shows how you would provide a username and password if your FTP server requires one.

Uploading a File

Once I had the script to delete a file working, I moved on to the next part of the task... figuring out how to actually upload a file. I naturally started with the code to delete a file that I had just gotten working. After all, I'd still need to specify the server and file name, specify the type of request, issue the request, and retrieve the response. The only difference this time is that the request needed to include the body of the file I wanted to upload.

    Protected Sub btnUploadFile_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim myFtpWebRequest As FtpWebRequest
        Dim myFtpWebResponse As FtpWebResponse
        Dim myStreamWriter As StreamWriter

        myFtpWebRequest = WebRequest.Create("ftp://ftp_server_name/filename.ext")

        'myFtpWebRequest.Credentials = New NetworkCredential("username", "password")

        myFtpWebRequest.Method = WebRequestMethods.Ftp.UploadFile
        myFtpWebRequest.UseBinary = True

        myStreamWriter = New StreamWriter(myFtpWebRequest.GetRequestStream())
        myStreamWriter.Write(New StreamReader(Server.MapPath("filename.ext")).ReadToEnd)
        myStreamWriter.Close()

        myFtpWebResponse = myFtpWebRequest.GetResponse()

        litResponse.Text = myFtpWebResponse.StatusDescription

        myFtpWebResponse.Close()
    End Sub

Looking at the code above, you'll see much of it is the same as the previous code listing. The first real change is that the method has changed from "DeleteFile" to "UploadFile". I also specify to transfer the file in binary mode.

Note: Binary mode can be slower, but while text files will transfer fine as binary, the reverse is not true. If you try to transfer a binary file as text you'll most likely end up with a corrupt file.

The next step is to get a handle on the stream of data being sent with our request. To do that we use the GetRequestStream method of the FtpWebRequest object. The method returns a Stream object. Since our goal is to write the data from our file to the Stream, the next step is to attach a StreamWriter to the Stream to make writing to it easier. We then call the Write method of the StreamWriter to write the data from the file to the RequestStream. Once we've written the data to the stream we close the stream and call the GetResponse method of our FtpWebRequest object just like we did before.

You may have noticed I glossed over one (no so) little thing -- how do we get the contents of the file into the StreamWriter. There are quite a few different ways, but the way I prefer, and the one shown above, is to simply use a StreamReader to read the file directly off the file system and pass it to the StreamWriter. The way I think of it, it's sort of like using a faucet to water flowers. Sure you can get a bucket, fill it with water, and then carry the bucket to the flowers, but it's a lot easier to simply hook a hose to the faucet and turn on the water. In our scenario it's connecting a StreamReader's Read method directly to a StreamWriter's Write method, but you get the picture. Why store the file's contents in a temporary byte array if you don't need to?

Downloading a File

The upload part was all I really needed, but I realize that that's really only about half the story for most people looking to do FTP from a web page. So here's the listing for downloading a file as well.

    Protected Sub btnDownloadFile_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim myFtpWebRequest As FtpWebRequest
        Dim myFtpWebResponse As FtpWebResponse
        Dim myStreamWriter As StreamWriter

        myFtpWebRequest = WebRequest.Create("ftp://ftp_server_name/filename.ext")

        'myFtpWebRequest.Credentials = New NetworkCredential("username", "password")

        myFtpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile
        myFtpWebRequest.UseBinary = True

        myFtpWebResponse = myFtpWebRequest.GetResponse()

        myStreamWriter = New StreamWriter(Server.MapPath("filename.ext"))
        myStreamWriter.Write(New StreamReader(myFtpWebResponse.GetResponseStream()).ReadToEnd)
        myStreamWriter.Close()

        litResponse.Text = myFtpWebResponse.StatusDescription

        myFtpWebResponse.Close()
    End Sub

It's pretty much exactly the reverse of uploading. We're dealing with the ResponseStream instead of the RequestStream and we're reading from a stream to a file instead of reading from a file to a stream.

The Whole Picture

Here's the full listing for the sample page I wrote for this article.

<%@ Page Language="VB" EnableViewState="False" Debug="True" %>
<%@ Import Namespace="System.Net" %>
<%@ Import Namespace="System.IO" %>
<script runat="server">

    Protected Sub btnUploadFile_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim myFtpWebRequest As FtpWebRequest
        Dim myFtpWebResponse As FtpWebResponse
        Dim myStreamWriter As StreamWriter

        myFtpWebRequest = WebRequest.Create("ftp://ftp_server_name/filename.ext")

        'myFtpWebRequest.Credentials = New NetworkCredential("username", "password")

        myFtpWebRequest.Method = WebRequestMethods.Ftp.UploadFile
        myFtpWebRequest.UseBinary = True

        myStreamWriter = New StreamWriter(myFtpWebRequest.GetRequestStream())
        myStreamWriter.Write(New StreamReader(Server.MapPath("filename.ext")).ReadToEnd)
        myStreamWriter.Close()

        myFtpWebResponse = myFtpWebRequest.GetResponse()

        litResponse.Text = myFtpWebResponse.StatusDescription

        myFtpWebResponse.Close()
    End Sub

    Protected Sub btnDownloadFile_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim myFtpWebRequest As FtpWebRequest
        Dim myFtpWebResponse As FtpWebResponse
        Dim myStreamWriter As StreamWriter

        myFtpWebRequest = WebRequest.Create("ftp://ftp_server_name/filename.ext")

        'myFtpWebRequest.Credentials = New NetworkCredential("username", "password")

        myFtpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile
        myFtpWebRequest.UseBinary = True

        myFtpWebResponse = myFtpWebRequest.GetResponse()

        myStreamWriter = New StreamWriter(Server.MapPath("filename.ext"))
        myStreamWriter.Write(New StreamReader(myFtpWebResponse.GetResponseStream()).ReadToEnd)
        myStreamWriter.Close()

        litResponse.Text = myFtpWebResponse.StatusDescription

        myFtpWebResponse.Close()
    End Sub

    Protected Sub btnDeleteFile_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim myFtpWebRequest As FtpWebRequest
        Dim myFtpWebResponse As FtpWebResponse

        myFtpWebRequest = WebRequest.Create("ftp://ftp_server_name/filename.ext")

        'myFtpWebRequest.Credentials = New NetworkCredential("username", "password")

        myFtpWebRequest.Method = WebRequestMethods.Ftp.DeleteFile

        myFtpWebResponse = myFtpWebRequest.GetResponse()

        litResponse.Text = myFtpWebResponse.StatusDescription

        myFtpWebResponse.Close()
    End Sub

</script>
<!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>
    <title>Sample FTP Operations</title>
</head>
<body>
<form id="myForm" runat="server">
<div>

<asp:Button ID="btnUploadFile" runat="server"
    OnClick = "btnUploadFile_Click"
    Text    = "Upload File"
/>

<asp:Button ID="btnDownloadFile" runat="server"
    OnClick = "btnDownloadFile_Click"
    Text    = "Download File"
/>

<asp:Button ID="btnDeleteFile" runat="server"
    OnClick = "btnDeleteFile_Click"
    Text    = "Delete File"
/>

<pre>
<asp:Literal id="litResponse" runat="server" />
</pre>

</div>
</form>
</body>
</html>

Please note that this is sample code. I've left out even the most basic error handling to keep things simple and easy to follow. Here's a list of some of the most common errors you're likely to encounter and should plan to handle:

  • Is the URI provided actually an FTP address (ftp://) and not HTTP (http://)?
  • Is the FTP server name able to be resolved via DNS?
  • Do you need to specify credentials to log in to the FTP server?
  • Do you have permission to upload/download/delete a file on the FTP server?
  • Do you have NTFS permission to write a downloaded file to the server's file system?
  • Are you trying to upload/download/delete a file that doesn't exist?
  • If you're moving large files, have you thought about the transfer time involved?

They're all relatively easily addressed, but which ones you encounter and how you handle them will depend on your specific scenario.

Conclusion

I hope this article has shown you just how easy the .NET Framework makes it to handle FTP transfers. In the past you were forced to find, purchase, and configure a third-party component to handle this type of thing. These days it's just another tool in the box.



Comments

  • i don't know any thing about FTP in asp.net

    Posted by bilalbinamar on 02/19/2014 12:42am

    i don't know any thing about FTP in asp.net will you help me to make a small web form, i want to make a page in asp.net using FTP for uploading and downloading a file, such that user are able to make file of 2GB to upload and download as well please help me sir................ wating for your reply sir

    Reply
  • wheloltabotly PumeSonee Phobereurce 8865726

    Posted by TizefaTaNaday on 06/13/2013 02:59am

    Fivahicibra airjordan1retrohighhofhalloffame.holidaygiving.org KapreapypeMom airjordanvtokyo23sale.holidaygiving.org maymnadrapymn

    Reply
  • What about C#

    Posted by One-Is on 04/30/2013 11:36am

    Hi! First, thank you for your post, it's really helpful! I made it work with a Raspberry Pi. But here's a little problem: your code works in VB, but when I tried translating it in C#, there seems to be some problems with the FtpWebRequests et StreamWriters... And my main project, which I wouldn't want to change entirely, is coded in C#. Are you as good in C# as you are in VB? It would be so great if you could help me out!

    Reply
  • 43554

    Posted by br on 08/18/2012 11:39am

    /// /// Example call : if (FtpUpload(FileUploadControl1, "ftp.my.com/somePathDir", @"user", "pass!", "domain")) /// /// /// /// /// /// private bool FtpUpload(FileUpload file, string ftpServer, string username, string ftpPass, string domainName = "") { // ftp://domain\user:password@ftpserver/url-path // If you are a member of a domain, then "ftp://domain-name\username:password@url-path" may fail because the backslash (\) is sent in as a literal character and Internet Explorer incorrectly looks for a file instead of parsing a Web address. Changing the backslash (\) in the domain-name\username to domainname\username works correctly. try { string ftpAddres; if (domainName != string.Empty) ftpAddres = "ftp://" + domainName + @"\" + username + ":" + ftpPass + "@" + ftpServer + "/" + file.FileName; else ftpAddres = "ftp://" + username + ":" + ftpPass + "@" + ftpServer + "/" + file.FileName; using (var webClient = new System.Net.WebClient()) { webClient.UploadData(new Uri(ftpAddres), file.FileBytes); } } catch (Exception e) { throw new Exception(e.Message, e); } return true; }

    Reply
  • Developer

    Posted by A. Nze on 07/13/2012 02:56am

    Great Stuff! I quite like the simplicity. Regards

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Live Event Date: November 13, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT APIs can be a great source of competitive advantage. The practice of exposing backend services as APIs has become pervasive, however their use varies widely across companies and industries. Some companies leverage APIs to create internal, operational and development efficiencies, while others use them to drive ancillary revenue channels. Many companies successfully support both public and private programs from the same API by varying levels …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds