DownStream - A file upload and download utility

Jason Haley
http://dotnetjunkies.com/weblog/jhaley


May 1, 2004

Summary
DownStream is a file upload and download utility that is designed to save the files in a database (not on the file system).  This utility was written to solve three problems I am faced with in everyday life: moving XSLT files to production, the need for some reusable file upload/download code, and need to optimize disk space and file space on my site.  In this article I walk you through setting up the demo site and then explain the four parts of the demo: saving a file, getting a file listing, deleting a file and creating a file on the fly.

Download the source code for this article

The problems DownStream is built to address
Downstream is a side project I started a while ago as an idea - code didn't really start until recently.  The idea came more out of curiosity and some frustration than anything else.  Overtime I have molded it into solving the three problems described below.

Moving XSLT files to production
Over the past few years I have written several web applications that used XML and XSLT, which leads to a problem - the problem is the change control process.  The change control process (as I am sure many of you are familiar with) deems it necessary to have all files that reside on the file system (xml or xslt in this case) of a production server go through the change control process on its way to production...sort of a limbo.  Once it makes it through limbo and into production everything is great.  Problem is it is hard to predict how long one will spend in limbo.  If I would have used a database implementation to stick the XML/XSLT files in a table as well as built an administration module to allow me to do this from the application, I could by-pass limbo all together, due to no files needing pushed to production.

Need for some reusable file upload/download code
In the past few years I have also build a few web applications that allowed file uploads and downloads.  For Dave Platt's class, there was an ASP.net homework that required the students to build an upload web page to save an image in the database and an HttpHandler to pull the image out of the database...this I liked.  Since helping with that homework, I have been meaning to write a sort of utility that would be easy to use and include a little more functionality, but based on the same idea of taking a file from the client and saving it in the database.

Disk space and file space
Recently I set up http://www.jasonhaley.com , where I have a ration of space on both the file system and a database.  In order for me to optimize the usage of the file system and the database, I need a utility that will make it easy for me to use either the file system or the database.  This utility needs to make it invisible to the user as to where the file really resides (file system or database).

How to setup the demo
The implementation of version 1.0 is pretty simple. The HttpModule checks to see if the file should be returned from the file system or the database and returns the file if it can.

1.  Unzip the files somewhere on your local drive

2.  Create a new web application (virtual directory), using your IIS Manager (MMC) pointing to your DownStream directory.  You might also want to set Default.aspx as the default page, while your there.

3.  Configure IIS wildcard mapping to the aspnet_isapi.dll, a good explanation of how to do this can be found here: http://scottwater.com/blog/articles/Wildcard.aspx.  Note: Make sure you set it to not verify if the file exists.

4.  Create a new SQL Server Database or use and existing database.

5.  Run the DbTable.sql file (can be found in data directory).  Note: if you change the user from the script included (webuser), you will need to make sure the user has the proper permissions.

6.  Configure your DBConnectionString in your web.config file to include the correct information.

What does the demo demonstrate?
With Default.aspx I have implemented code to do the following:

Uploading or "Saving" a file
The uploading of a file is pretty easy on the user's side of things.  Just click the Browse button, choose a file and click the upload button (screen print shown below):

I wanted this code library to abstract the little details of what it takes to save the file to  the database... so all you have to do (as show in the code below) is pass the HttpPostedFile off to the Save method.

private void Page_Load(object sender, System.EventArgs e)
{
  // files are posted back, so only check those for files
  if (IsPostBack)
  {
    if (Request.Files.Count > 0 && Request.Files[0].FileName.Length > 0)
    {
       FileUtility.Save(Request.Files[0]);
    }
  }
}

NOTE: Current implementation will not handle duplicates.  If the file name is not unique, then it crashes.  I be will releasing a version soon that does not crash when a file name is not unique (or you can write the code yourself).  I am still working on what the business rule is going to be when there is a file that already exists by that name.

Create a listing of files in the database as hyperlinks
In order to build a list of hyperlinks of all the files in the database (sort of a directory listing), we need to do a little more work, but in the end we end up with an HTML table of Hyperlinks line see below:

In order to get all the files that are currently in the database all you need to do is call the GetFileList(appPath).  Currently the appPath is not saved with the file, so the only reason you would pass anything other than an empty string is if you wanted the href in the hyperlinks to include a complete path.  The majority of this file listing code (shown below) is the building of the table and hyperlinks.

private void PopulateList()

{
  // clean out the link list first
  this.linkList.Controls.Clear();
 
  // get a file listing from the utility class
  ListDictionary files = new ListDictionary();
  files = FileUtility.GetFileList(string.Empty);
     
  // if filename where returned build the list of names in a table
  if (files.Count > 0)
  {
    System.Web.UI.WebControls.HyperLink hl = null;
    System.Web.UI.WebControls.Table table = new Table();
    System.Web.UI.WebControls.TableRow tr = null;
    System.Web.UI.WebControls.TableCell td = null;
    foreach (DictionaryEntry de in files)
    {
      hl = new HyperLink();
      hl.NavigateUrl = de.Key.ToString();
      hl.Text = de.Value.ToString();
     
      td = new TableCell();
      td.Controls.Add(hl);
           

      tr = new TableRow();
      tr.Cells.Add(td);
                 
      table.Rows.Add(tr);    
    }
    this.linkList.Controls.Add(table);
  }
}

Delete a file
Since the current implementation does not allow for duplicates, the call to delete a file is real simple... call the Delete method and pass in the name of the file you want to delete.

Below is the code for the Delete button.  As can be seen, the majority of the code has nothing to do with removing the file from the database.

private void btnDeleteFile_Click(object sender, System.EventArgs e)
{
  // make sure a filename was typed
  if (txtFileName.Text.Length > 0)
  {
    FileUtility.Delete(txtFileName.Text);
    // Repopulate the link list
    PopulateList();
    // make sure the text is cleaned out
    txtFileName.Text = "";
  }
}

Create an XML file on the fly
One of the problems I ran into soon after I though I had version 1.0 done, was the fact that sometimes I have some XML coming back from a database call and I need to stick it into the database as a file too. This problem is a little different due to no file being posted to the server.  So I added an overload for the Save method that takes in the filename, text to save as the file and the content type.

Saving an XML string as a file in the database is just as easy as saving a posted file now.

private void btnCreateXmlFile_Click(object sender, System.EventArgs e)
{
  if (txtXmlFile.Text.Length > 0 && txtXmlFileName.Text.Length > 0)
  {
    string fileName = txtXmlFileName.Text;
    string fileText = txtXmlFile.Text;
    string contentType = "text/xml";
     
    // create and save the file as xml
    FileUtility.Save(fileName, fileText, contentType);
    // Repopulate the link list
    PopulateList();
    // make sure the text is cleaned out
    txtXmlFile.Text = "<root><element>test</element></root>";
  }
}

NOTE: The XML is not checked to be in good form before it is saved...this is currently up to you, just like you can save a badly formed XML file to the file system.

Conclusion
In the end I feel this new utility class is going to save me a lot of time.  You don't have to always use the GetFileHttpModule and tie the library to ASP.Net, you can just use the FileUtility as it is.  Actually the HttpModule uses the FileUtility to do its work with opening files from a database.  So if you need to save an uploaded file to a database, access that file afterwards or delete it, then hopefully you can find this code useful in your tool box (or extend it to do what you need to do).

There are already a number of enhancements I could make to this library, such as handle duplicate file names.  Another excellent enhancement would be to create a custom control that utilized the library so all I would have to do is drag the control on to the web form, but at this time I will have to leave that as an exercise to the user (or until I get time to put on it).

Resources Consulted
Scott Mitchell's URL Rewriting in ASP.NET at MSDN http://msdn.microsoft.com/asp.net/using/building/web/default.aspx?pull=/library/en-us/dnaspp/html/URLRewriting.asp

Scott Watermasysk's .Text http://scottwater.com/blog/archive/category/71.aspx

Developing Microsoft ASP.Net Server Controls and Components, by Nikhil Kothari, Vandana Datye http://www.amazon.com/exec/obidos/tg/detail/-/0735615829/qid=1083285165/sr=1-2/ref=sr_1_2/103-7010992-1584618?v=glance&s=books