Pages

Advertisement

Thursday, August 16, 2007

File Uploading Using ASP.NET

In the golden olden days of ASP, managing a file upload was pretty difficult. Most developers reverted to digging deep in their wallets to purchase a third-party add-on to help them achieve the desired result. No longer.

With ASP.NET, you can now upload files with practically a few lines of code. And the following four easy-to-follow steps show you exactly how to do it.

  1. Add a File Field control to your form. You'll find this under the HTML tab on the toolbox. You'll have seen this control when uploading attachments through Hotmail, or when sending files to a Web site.
  2. Right-click on the File Field control and check the Run as Server Control option. This allows you to manipulate the control in code, sort of like a less-functional ASP.NET server control.
  3. Change the ID of your File Field control to something more understandable, such as "fileUpload".
  4. Enter the HTML view of your Web form and find the opening <form> tag. You'll need to edit this to add the parameter encType="multipart/form-data". Your <form> tag may look something like this when you're finished:

<form id="Form1" method="post" encType="multipart/form-data" 
   runat="server">


And that's it. You've set up your form to receive file uploads. But after the user has selected a file and submitted your form, how do you manipulate the sent file? The easiest technique is to run a simple line of code, like this:

NameOfFileFieldElement.PostedFile.SaveAs( _
Server.MapPath("uploadedfile.txt"))



Pretty simple, really. You might also want to check that the user has uploaded a valid file first, before saving (unless you're really into errors). The following function does this for you, checking for null uploads and zero byte files: Public Function FileFieldSelected(ByVal FileField As _
  System.Web.UI.HtmlControls.HtmlInputFile) As Boolean
    ' Returns a True if the passed
    ' FileField has had a user post a file
    If FileField.PostedFile Is Nothing Then Return False
    If FileField.PostedFile.ContentLength = 0 Then Return False
    Return True
End Function
TOP TIP
If you get an "access denied" error when trying to save files directly to your Web application folder, go check your permissions. Ensure that your virtual directory in IIS has read and write permissions. (Change this through IIS.) You may also want to ensure that your ASPNET, guest, or impersonated accounts have appropriate permissions, both for computer access and for the actual folder itself. (To change this, right-click on the folder, select Sharing and Security, and then select the Security tab.)

The problem is that both you and I know that 95% of people reading this don't really want to go ahead and store files directly on the server file system. Rather, you want to save information into your database, into that SQL Server "image" field.

Every article I've seen so far manages to conveniently skip this topic. But not this one...


Figure: Click on the button and pick a file!

Storing Uploaded Files in Your Database

Firstly, a few tips on storing files inside your SQL Server database.

For convenience, you'll really need to store at least three bits of information about your file to get it out in the same shape as you put it in. I'd suggest "data" (a field that will hold your actual file as a byte array, data type "image"), "type" (a field to hold details of the type of file it is, data type "varchar"), and "length" (a field to hold the length in bytes of your file, data type "int").

I'd also recommend "downloadName", a field to hold the name that the file had when it was uploaded, data type "varchar". This helps suggest a name should the file be downloaded again via the Web.

The problem you have is translating the information from the File Field control into an acceptable format for your database. For a start, you need to get your file into a byte array to store it in an image field. You also need to extract the file type, length, and the download name. Once you have this, set your fields to these values using regular ADO.NET code.

So, how do you get this information? It's simple: just use the following ready-to-run code snippets, passing in your File Field control as an argument. Each function will return just the information you want to feed straight into your database, from a byte array for the image field to a string for the file type.


Public Function GetByteArrayFromFileField( _
  ByVal FileField As System.Web.UI.HtmlControls.HtmlInputFile) _
  As Byte()
  ' Returns a byte array from the passed 
  ' file field controls file
  Dim intFileLength As Integer, bytData() As Byte
  Dim objStream As System.IO.Stream
  If FileFieldSelected(FileField) Then
      intFileLength = FileField.PostedFile.ContentLength
      ReDim bytData(intFileLength)
      objStream = FileField.PostedFile.InputStream
      objStream.Read(bytData, 0, intFileLength)
      Return bytData
  End If
End Function
 
Public Function FileFieldType(ByVal FileField As _
  System.Web.UI.HtmlControls.HtmlInputFile) As String
    ' Returns the type of the posted file
    If Not FileField.PostedFile Is Nothing Then _
      Return FileField.PostedFile.ContentType
End Function
 
Public Function FileFieldLength(ByVal FileField As _
  System.Web.UI.HtmlControls.HtmlInputFile) As Integer
    ' Returns the length of the posted file
    If Not FileField.PostedFile Is Nothing Then _
      Return FileField.PostedFile.ContentLength
End Function
 
Public Function FileFieldFilename(ByVal FileField As _
  System.Web.UI.HtmlControls.HtmlInputFile) As String
    ' Returns the core filename of the posted file
    If Not FileField.PostedFile Is Nothing Then _
      Return Replace(FileField.PostedFile.FileName, _
      StrReverse(Mid(StrReverse(FileField.PostedFile.FileName), _
      InStr(1, StrReverse(FileField.PostedFile.FileName), "\"))), "")
End Function
Sorted! One question remains, however. Once you've got a file inside a database, how do you serve it back up to a user? First, get the data back out of SQL Server using regular ADO.NET code. After that? Well, here's a handy function that'll do all the hard work for you. Simply pass it the data from your table fields and hey presto: 
 
Public Sub DeliverFile(ByVal Page As System.Web.UI.Page, _
  ByVal Data() As Byte, ByVal Type As String, _
  ByVal Length As Integer, _
  Optional ByVal DownloadFileName As String = "")
    ' Delivers a file, such as an image or PDF file,
    ' back through the Response object
    ' Sample usage from within an ASP.NET page:
    ' - DeliverFile(Me, bytFile(), strType, intLength, "MyImage.bmp")
    With Page.Response
      .Clear()
      .ContentType = Type
      If DownloadFileName <> "" Then
          Page.Response.AddHeader("content-disposition", _
            "filename=" & DownloadFileName)
      End If
      .OutputStream.Write(Data, 0, Length)
      .End()
    End With
End Sub

Simply pass it your byte array, file type, and length, and it'll send it straight down to your surfer. If it's an image, it'll be displayed in the browser window. If it's a regular file, you'll be prompted for download.

If it's made available for download, this function also allows you to specify a suggested download file name, a technique that many ASP.NET developers spend weeks trying to figure out. Easy!

No comments:

Post a Comment