Uploading and Downloading Files
  
    A lot of applications require information to be transferred in the form of data
    files. For example, a prospective employee may want to upload a CV in Microsoft
    Word format to an employment agency's website. Likewise, an employer may want to
    download CVs of prospective employees. There are lots of applications potentially
    requiring file transfer; from software download to image bureaux.
  
    File upload can be achieved in a straightforward way by using a FileUpload control.
    This control only requires a small amount of code to cause an uploaded file to be
    stored on the file system of the server. However, it is sometimes preferable to
    store uploaded files in a database. This is more complex, but both techniques will
    be presented below, together with a mechanism to download a file which may or may
    not have been previously uploaded.
  
    Uploading using a folder on the server
  
    For this example we will assume there is a folder in the home directory for the
    application called 'upload'.
  
    - Close any open project and create a new C# ASP.NET Website called Upload
 - In Solution Explorer create a new folder called Upload in the application directory.
 - Drag a FileUpload control onto the default web page.
 - Drag a Button onto the web page and change its label to 'Upload File'.
 - Double click the button, to bring up the code window with an empty button click
    event handler for the button, as follows:
protected void Button1_Click(object sender, EventArgs e)
{
}
           
    - We need code to check that a file has been selected by the user, and if so code
      to accept the file and store it in the Upload folder. The FileUpload control has
      several properties and methods which allow us to do this quite easily. FileUpload
      has a property HasFile which returns true if a file is available. The SaveAs method
      simply needs the full path for the file when it is placed on the server. In this
      case we need to make up the file path by adding the bare filename of the file to the
      actual location of the Upload folder. The bare filename is given by the FileName
      property of the FileUpload control. There is a predefined method called Server.MapPath
      which will return an actual path if you give it a URL on the server. In this case
      the URL is '~/Upload/'. Note: we have used the '~' symbol before to denote the home
      folder for the application. The complete code is:
      
protected void Button1_Click(object sender, EventArgs e)
{
  if (FileUpload1.HasFile)
    FileUpload1.SaveAs
        (Server.MapPath("~/Upload/") + FileUpload1.FileName);
}
     
  
  
    These two lines of code perform the complete file upload process.
	
    Security notes
	
    You may think there are security issues with this scenario, and you are 
	right. However, all is not as straightforward as it may seem. The end user 
	of your site only has permission to view your pages, so you may ask how they 
	can save a file on the server. The answer is that it is not the end user 
	saving the file it is the ASP.NET server process. Security is achieved by 
	making sure that:
	
		- The server process has permissions to save files in the target 
		folder.
 
		- The application doesn't allow the end user to choose arbitrary 
		locations to save the uploaded file.
 
		- The end user doesn't have permissions to execute the uploaded file 
		on the server using web based access.
 
		- Restricting the types of file that can be uploaded.
 
	
	The above points are addressed by the web or network administrator who 
	performs the configuration of the application.
  
    Downloading a file
  
    The simplest form of download uses exactly the same mechanism as a hypertext link.
    In our page we can include a link, whose URL is the name of the file. This can be
    created dynamically from a list of files in a folder.
  
    However, the problem with
    this solution is that the file may automatically open in the user's browser, replacing
    the current page. Files which are not recognised by the browser (e.g. .EXE files)
    will cause the browser to prompt the user to Open/Run
    or Save the file, as follows:
  
    
 
  
    An example of the simple download is given in the
    ZIP version of Download.
  
    A more controlled solution to the download problem forces the browser to display
    a file download security warning and provides the filename ready to save the file
    to your local file system.
  
    When you retrieve a document from the Internet it is packaged in an HTTP Response
    packet. The packet has a header which specifies the content type. This will usually
    be an HTML file or an image such as a GIF or JPG file. Content types are specified
    using a naming scheme called MIME (Multi-purpose Internet Mail Extensions) which
    was originally created for transfer of files using email. A
    MIME type has two parts separated by a solidus ('/'). The first part is a generic
    type, the second part is the specific variant. An HTML page has MIME type 'text/html'.
    A .jpg image has MIME type 'image/jpeg' and a .gif image has type 'image/gif'.
  
    If we want to allow a user to save a file that is being downloaded we need to return
    the file with a header that tells the users browser that it is a data file and shouldn't
    be displayed. The usual MIME type for this is 'application/octet-stream'. This signals
    the browser that binary data is being sent. The browser will respond by asking the
    user if they want to save the file or open/run it. You can also send a header as part
    of the HTTP response which tells the browser that a file is being sent and also
    gives its name. This header type is named 'Content-Disposition'. Assume we have
    a webpage with a TextBox (txtFilename), a Button (butDownload) and a Label (labMessage).
    The user can type a filename in the TextBox
    (not very realistic), and when the Button is clicked the file will be downloaded.
    The code to handle this scenario looks like this:
  protected void butDownload_Click(object sender, EventArgs e)
{
  if (System.IO.File.Exists
       (Server.MapPath("~/Upload/") + txtFilename.Text))
  {
    Response.Clear();
    Response.ContentType = "application/octet-stream";
    Response.AddHeader
       ("Content-Disposition",
        "attachment; filename=\"" + txtFilename.Text + "\"");
    Response.Flush();
    Response.WriteFile
       (Server.MapPath("~/Upload/") + txtFilename.Text);
    labMessage.Text = "";
  }
  else
  {
    Message.Text = "File does not exist";
  }
}
  You can download a
  ZIP version of the ForceDownload application
	.
    The code to fill a list box with the names of files is relatively 
	straightforward. The following code shows how to list all .jpg files from 
	the 'upload' folder. The method Path.GetFiles() returns an array of strings 
	containing the fully qualified filename (including drive letter and folder 
	names) for each of the matching files.
	  foreach (string fname in
    Directory.GetFiles(Server.MapPath("~/upload"),
                       "*.jpg", 
                       SearchOption.TopDirectoryOnly))
      {
        ListBox1.Items.Add(Path.GetFileName(fname));
      }
	
    Note: if you want to allow the end user to select a file in the list a press 
	abutton to down load there is one thing to watch for. You must make sure the 
	listbox gets filled with the filenames just once (not every time the page 
	loads) otherwise the page can't remember which file was selected. You can 
	download a ZIP version of the 
	ListBoxDownload application which uses this technique.
	
    Uploading with a database
  
    The previous sections have ignored the problems of keeping track of files which
    have been uploaded, and of giving the user some indication of which files are available
    for download in a dynamic way. A more realistic scenario would mean that uploaded
    files should be logged in a database, so that the current list can be made available
    to the user. There, is still an important decision to be made, and that is whether
    to save the uploaded file in the database, or whether to simply save a reference
    to the file in the database and store the files in a separate folder. Both possibilities
    have advantages and disadvantages, but both are still viable. The choice probably
    depends on circumstances.
  
    Database requirements - File saved as part of record
  
    The file will be saved as in a field as a binary image, so we will also need to
    keep track of other information about the file. This will be the filename, its content-type,
    and the size of the file. We cannot use the filename as the key field, as there
    is a good chance of files being uploaded with the same filename, so we should use
    an autonumber field (identity field in MS SQL) to avoid duplicate keys. If filenames
    are duplicated, we may also want to store further information about the file. For
    example, an employment agency may allow CVs to be uploaded, so their may be a field
    identifying the employee. Another example might be an image repository where there
    may need to be fields for the identity of the photographer, a description of the
    image and the date taken.
  
    Database requirements - File saved separate from the database
  
    In this scenario we will need a unique key for each record, which again could be
    done with an autonumber field. However, Since the files are being saved independently
    of the database we will need to use a unique filename to save each file. A simple
    solution to this is to store the file using the key value as its name, and store
    the original filename as part of the record. We still need to have fields for 'content-type'
    and file size. We may also want identification information for the 'owner' of the
    file and other descriptive information. 
  
    Comparison
  
    The only real difference between the table structures for the scenarios is the presence/absence
    of a field for the file data. However, the separate file solution poses a problem
    when implementing the code to save the record and save the file as we shall see
    later.
  
    Uploading and downloading files saved in the database.
  
    Before looking at the process in detail, let us look at the solution from the point
    of view of the design and development process.
  
    - Web form design: a simple interface is all that is required for file upload (FileUpload
      control and an Upload button), and all the work will be done within the click event
      handler for the Upload button. However, file download ought to be made convenient
      for the user by providing a list of files for download. This can be done by having
      a GridView control listing the records in the database. We will need to trim the
      GridView so that it doesn't display the FileData column as it is not a simple field
      (e.g. text or numeric). We can enable the Select command on the GridView. If we
      change the text from 'Select' to 'Download' this provides a simple interface to
      allow the user to select and download a file for download in one action. We can
      also enable the Edit, Paging and Sorting commands to maximize the usefulness of
      the interface. 
 
    - Script code: We only have two actions to code on the web form. These are the Upload
      File button and the SelectedIndexChanged action on the GridView. 
 
    - Upload Code: We must connect to the database, retrieve the filename, content type,
      file size and file data from the upload control. Then we create a parameterised
      SQL insert command for creating a new record in the database. We set the values
      of the parameters and execute the command. Finally we must refresh the GridView
      control so that it displays the grid with the newly inserted record. 
 
    - Download code: We must get the filename from the selected row of the GridView.
      We can then connect to the database, build an SQL select statement to retrieve the
      record corresponding to the selected filename. We execute the select statement and
      retrieve the values, including the file data from the retrieved record. Finally
      we can send the response to the client by creating a new response packet, much like
      the non-database version of the file upload/download example. 
 
  
  
    Step by step
  
    - Create a new C# ASP.NET Website called UploadDB. 
 
    - Create a new database, called Uploads.mdf with a table called tblFileData, matching
      the following structure:
      
      Make sure Filename and ContentType are large enough to hold meaningful data (minimum
      of 50 characters), and note the Identity properties for the FileId field.  
    - Build a user interface matching the following display, noting that you may initially
      have four columns displayed in your GridView, and Download links may read 'Select':
      
     
    - If you have got four columns (FileData is displayed) you will need to amend the
      properties of the GridView to hide the FileData column. Use the SmartTag dialog
      and choose Edit Columns
      
     
    - In the Selected Fields section choose FileData. In the Bound Field Properties
      section set the Visible property to False. 
 
    - If the GridView has 'Select' links you will need to change the text to 'Download'.
      Using the Edit Columns dialog (from stage 4) choose the Command Field and edit BoundField
      properties. Change the SelectText property to 'Download'. 
 
    - Your web form should now be the same as in item 3. 
  
  
    Now we need to write the script code to perform the upload and download.
  
    First the upload.
    Select the Upload File button and double click it. You will get an empty event
      handler for the button.  
    - The pseudo code for the event handler is as follows:
      
If the upload control has a file attached
    Get the filename, content type, file size 
        and file data from it
    Open a connection to the database
    Build an SQL command for the insert
    Copy the data into the SQL command
    Execute the SQL command
    Update the GridView control
     
    - Note: we already have a connection string and an insert SQL command in the SQLDataSource
      we used for the GridView. This means we can copy these when we need to use them. 
      First put this library reference at the top of your .cs file.
      
      using System.Data.SqlClient
 - 
      Below is the actual code
      
void Button1_Click(object sender, EventArgs e)
{
  // If the upload control has a file attached
  if (FileUpload1.HasFile)
  {
    // Get the filename, content type, file size
    // and file data from it
    String Filename = FileUpload1.FileName;
    String ContentType = FileUpload1.PostedFile.ContentType;
    int FileSize = FileUpload1.PostedFile.ContentLength;
    Byte[] FileData;
    FileData = new Byte[FileSize];
    FileUpload1.FileContent.Read(FileData, 0, FileSize);
    // Open a connection to the database
    SqlConnection Conn = 
        new SqlConnection(SqlDataSource1.ConnectionString);
    Conn.Open();
    // Build an SQL command for the insert
    SqlCommand cmd = 
        new SqlCommand(SqlDataSource1.InsertCommand, Conn);
    // Copy the data into the SQL command
    cmd.Parameters.Add
       ("@Filename", SqlDbType.Text, 50).Value = Filename;
    cmd.Parameters.Add
       ("@ContentType", SqlDbType.Text, 50).Value = ContentType;
    cmd.Parameters.Add
       ("@DataSize", SqlDbType.Int).Value = FileSize;
    cmd.Parameters.Add
       ("@Filedata", SqlDbType.Image, FileSize).Value = FileData;
    // Execute the SQL command
    cmd.ExecuteNonQuery();
    Conn.Close();
    // Update the GridView control
    SqlDataSource1.Select(new DataSourceSelectArguments());
    GridView1.DataBind();
  }
}
  
    Now we need to write the script code for downloading a file once it is selected
    in the GridView. When the user selects a row in a GridView, the SelectedIndexChanged
    event is fired. So we need a handler for this event. He we go!
     
    - Select the GridView control on your form and choose the Event icon in the properties.
      Double click the SelectedIndexChanged event and you will get an empty event handler.
    
 
    - The pseudo code for downloading a file is:
      
Get the filename from the selected row of the GridView
Open a connection to the database
Build an SQL select statement to retrieve the record
Copy the filename into the command    
Execute the SQL statement
Get the file size and file data from the retrieved record
Send the retrieved file to the client
     
    - The code for this is given below: 
  
void GridView1_SelectedIndexChanged(object sender, EventArgs e)
{
  // Get the filename from the selected row of the GridView
  GridViewRow row = GridView1.SelectedRow;
  string Filename = row.Cells[1].Text;
  // Open a connection to the database
  SqlConnection Conn = 
    new SqlConnection(SqlDataSource1.ConnectionString);
  Conn.Open();
  // Build an SQL select statement to retrieve the record
  SqlCommand cmd = 
   new SqlCommand
    ("Select * from [tblFileData] where [Filename] = @Filename;",
     Conn);
  // Copy the filename into the command
  cmd.Parameters.Add
    ("Filename", SqlDbType.NChar, 50).Value = Filename;
  // Execute the SQL statement
  SqlDataReader dr = cmd.ExecuteReader();
  dr.Read();
  // Get the file size and file data from the retrieved record
  int FileSize = dr.GetInt32(2);
  Byte[] FileData = new Byte[FileSize];
  dr.GetBytes(3, 0, FileData, 0, FileSize);
  Conn.Close();
  // Send the retrieved file to the client
  Response.Clear();
  Response.ContentType = "application/octet-stream";
  Response.AddHeader
      ("Content-Disposition",
       "attachment; filename=\"" + Filename + "\"");
  Response.Flush();
  Response.BinaryWrite(FileData);
}
 
  
    Download a
    ZIP version of UploadDB.
  
    Uploading and downloading files saved externally to the Database
  
    A different scenario will be used for this example so that we can show more features
    of the .NET environment. We will create a photo library which will only accept the
    upload of JPEG files with extension .jpg.
  
    Before looking at the process in detail, let us look at the solution from the point
    of view of the design and development process. The main difference between this
    approach and the previous one is the need to generate unique filenames when files
    are uploaded. The problem is that when we insert a new record, we need to know the
    autogenerated key for the new record so that we can use it for the filename. To
    make this work we have to use a special combination of SQL query statements. The
    first is the insert query and the second is a select statement which returns the
    value of the autogenerated key. As long as the two statements are executed together
    the second part returns the actual key value. For this example we will only use
    a simple table with a key field and a filename. The SQL for this is:
    INSERT INTO tblUploads (Filename) VALUES (@Filename);
SELECT @FileID = SCOPE_IDENTITY();
    
  The INSERT statement uses the value of the Filename from the Upload control.
  The SELECT statement receives the value of the key field into the Parameter @FileId.
  
    This value can be used to make the filename for storing the image in the upload
    folder.
  
    Download could be done as with the previous example, but we can make the interface
    more effective by having an image control on the web form and making it display
    the current selected image.
  
    Step by step
  
    - Create a new C# ASP.NET Website called UploadSeparate.
 - Create a new database,
      called Upload.mdf with a table called tblUploads, matching the following structure:
      
      Make sure Filename is large enough to hold meaningful data (minimum of 50 characters) 
	  and make the FileId an Identity field, like the previous example. - 
        Build a user interface matching the following display, noting that you may initially
        have four columns displayed in your GridView, and 'View' links may read 'Select':
        
          - Select the Upload File button and double click it. You will get an
          empty event handler for the button.
First put these library references at the top of your .aspx.cs file. 
using System.Data.SqlClient;
using System.IO;
         
     - Below is the actual code for the button click:
     
protected void Button1_Click(object sender, EventArgs e)
{
 Label1.Visible = true;
 if (FileUpload1.HasFile)
  if (System.IO.Path.GetExtension
          (FileUpload1.FileName).ToLower() == ".jpg")
  {
   SqlConnection conn =
     new SqlConnection(SqlDataSource1.ConnectionString);
   SqlCommand cmd =
     new SqlCommand
       (@"INSERT INTO tblUploads (Filename)
                 VALUES (@Filename);
          SELECT @FileID = SCOPE_IDENTITY();",
        conn);
   cmd.Parameters.Add
      ("@Filename",
       SqlDbType.NVarChar, 50).Value = FileUpload1.FileName;
   cmd.Parameters.Add("@FileID", SqlDbType.Int, 4);
   cmd.Parameters["@FileID"].Direction =
                                ParameterDirection.Output;
   conn.Open();
   cmd.ExecuteNonQuery();
   FileUpload1.SaveAs
       (Server.MapPath("~/Uploads/") + 
        cmd.Parameters["@FileID"].Value.ToString() + ".jpg");
   GridView1.DataBind();
   Label1.Visible = false;
  }
}
         
        - The code for Gridview.SelectedIndexChanged is quite straightforward. It simply retrieves
          the selected row, makes a URL from the key and sets the ImageUrl for the Image control.
          The old filename is used to set the AlternateText. The code looks like this:
          
protected void GridView1_SelectedIndexChanged
                 (object sender, EventArgs e)
{
  GridViewRow row = GridView1.SelectedRow;
  Image1.ImageUrl = "~/uploads/" + row.Cells[1].Text + ".jpg";
  Image1.AlternateText =
                 "Original filename: " + row.Cells[2].Text;
} 
Download a 
  ZIP version of the UploadSeparate application.