Managing FTP Transfers from an ASP.NET Web Page

CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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.



More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read