Multiple File Upload...

The HtmlInputFile and the FileUpload controls allow the user to upload files to the server. It would be nice to have a control that allows the user to upload more than one file at a time...


By: Brian Mains Spacer Date: February 25, 2006Spacer Download Spacer Download the code.

The HtmlInputFile and the FileUpload controls allow the user to upload files to the server. FileUpload is a 2.0 addition server control that has most of the same features of the HtmlInputFile control, but with a few added properties. However, it would be easier to have a control that allows the user to upload more than one file at a time, or to allow the user to add a specific number of input file boxes dynamically in the form.

Before we begin, you should be aware of some limitations. These controls do not store their viewstate; they implement the methods of IPostBackDataHandler; however, the LoadPostData method always returns false. Even overriding these methods yields no effect, as the value property for the file upload is read-only. I’ve done some research, and supposedly in newer browsers this could be set, but I have not come across a solution to do so.

If you read my article on the Collapsible Panel, you will have seen this event notation, where each event has an ING and an ED event, one firing before the action takes place (while also allowing you to cancel it), and one that fires afterward. This is the structure for events to add and clear input boxes in the control. The control also implements IRepeatInfoUser, to render the file upload boxes in a horizontal or vertical fashion, or creating a table structure. This just adds to the versatility of the control, even though in most situations, it won’t be desired.

In the rendering process, I render Add and Clear links using the header or footer sections of the render item. To determine this, the HasHeader and HasFooter methods of IRepeatInfoUser check the LinkLocation property enumeration.

<Browsable(False)> _
Public ReadOnly Property HasFooter() As Boolean Implements System.Web.UI.WebControls.IRepeatInfoUser.HasFooter
  Get
    Return (Me.LinkLocation = LinkLocationType.Bottom)
  End Get
End Property

<Browsable(False)> _
Public ReadOnly Property HasHeader() As Boolean Implements System.Web.UI.WebControls.IRepeatInfoUser.HasHeader
  Get
    Return (Me.LinkLocation = LinkLocationType.Top)
  End Get
End Property

Public Sub RenderItem(ByVal itemType As System.Web.UI.WebControls.ListItemType, ByVal repeatIndex As Integer, ByVal repeatInfo As System.Web.UI.WebControls.RepeatInfo, ByVal writer As System.Web.UI.HtmlTextWriter) Implements System.Web.UI.WebControls.IRepeatInfoUser.RenderItem
  writer.RenderBeginTag(HtmlTextWriterTag.Tr)
  writer.RenderBeginTag(HtmlTextWriterTag.Td)

  If (itemType = ListItemType.Header OrElse itemType = ListItemType.Footer) Then
    writer.AddAttribute(HtmlTextWriterAttribute.Href, "javascript:" & Page.ClientScript.GetPostBackEventReference(Me, "add"))
    writer.RenderBeginTag(HtmlTextWriterTag.A)
    writer.Write(Me.AddLinkText)
    writer.RenderEndTag()

    writer.Write("  ")

    writer.AddAttribute(HtmlTextWriterAttribute.Href, "javascript:" & Page.ClientScript.GetPostBackEventReference(Me, "clear"))
    writer.RenderBeginTag(HtmlTextWriterTag.A)
    writer.Write(Me.ClearLinkText)
    writer.RenderEndTag()

  ElseIf (itemType = ListItemType.Item OrElse itemType = ListItemType.AlternatingItem) Then
    Me.Controls(repeatIndex).RenderControl(writer)
  End If

  writer.RenderEndTag() '/td
  writer.RenderEndTag() '/tr
End Sub

The fileupload controls are created in CreateChildControls and rendered above as the item. The file boxes are based upon the InputBoxes property, which is an integer value, stating how many input boxes to use for the control. This value is added or reset through the AddNew and Clear methods, which AddNew adds one to it everytime, and Clear resets it back to one.

Protected Overrides Sub CreateChildControls()
  For intI As Integer = 1 To Me.InputBoxes
    Dim objBox As New FileUpload
    objBox.ID = "fu" & intI
    Me.Controls.Add(objBox)
  Next
End Sub

This control also makes use of the method GetPostBackEventReference in the RenderItem method. This method returns a javascript string with the function that will perform the posting back (which is rendered in a link’s href property). The references are shown below; we use two event arguments for this control: add and clear. RaisePostBackEvent handles the postback. It receives the event argument, and calls the appropriate method.

'In RenderItem method, rendered in the HREF property of an <a>
Page.ClientScript.GetPostBackEventReference(Me, "add")
Page.ClientScript.GetPostBackEventReference(Me, "clear")

Public Sub RaisePostBackEvent(ByVal eventArgument As String) Implements System.Web.UI.IPostBackEventHandler.RaisePostBackEvent
  Dim strArg As String = eventArgument.ToLower()
  If (strArg = "add" OrElse strArg = "addnew") Then
    AddNew()
  ElseIf (strArg = "clear") Then
    Clear()
  End If
End Sub

This control also makes use of IPostBackDataHandler and uses the LoadPostData method to check any possible uploaded files, and if there is one, raise an event. This allows the developer to handle the uploading of the file. As an alternative, this control could handle that action through an automated means by using an UploadFolder property, referencing a folder on the web server.

Public Function LoadPostData(ByVal postDataKey As String, ByVal postCollection As System.Collections.Specialized.NameValueCollection) As Boolean Implements System.Web.UI.IPostBackDataHandler.LoadPostData
  Me.EnsureChildControls()
  For Each objControl As Control In Me.Controls
    If (TypeOf (objControl) Is FileUpload) Then
      Dim objUpload As FileUpload = CType(objControl, FileUpload)
      If (objUpload.HasFile) Then
        OnInputBoxPosted(New PostedFileEventArgs(objUpload.PostedFile))
      End If
    End If
  Next
End Function

For the control to receive postbacks, you have to call Page.RegisterRequiresPostBack method. This will then allow LoadPostData to fire.

Protected Overrides Sub OnPreRender(ByVal e As System.EventArgs)
  MyBase.OnPreRender(e)
  If (Page IsNot Nothing) Then
    Page.RegisterRequiresPostBack(Me)
  End If
End Sub

This control works well with allowing users to upload multiple files. If using the links (add and clear), you may want to list a disclaimer stating that the user should add the desired number of textboxes first before browsing to all of the files, as they won’t be saved. Also, the control allows you to upload the files through a button of your own. If you don’t want the add/clear links shown, set the LinkLocation property to the None enumeration, and they will disappear.

This class comes with a PostedFileEventArgs that contains details about the posted file, which is made available to the user in the InputBoxPosted event. It is defined below.

Public Class PostedFileEventArgs
  Inherits System.EventArgs

  Private m_objFile As HttpPostedFile

  Public ReadOnly Property FileName() As String
    Get
      Return m_objFile.FileName
    End Get
  End Property

  Public ReadOnly Property FileStream() As Stream
    Get
      Return m_objFile.InputStream
    End Get
  End Property

  Public Sub New(ByVal objFile As HttpPostedFile)
    m_objFile = objFile
  End Sub

  Public Sub SaveAs(ByVal strFilePath As String)
    m_objFile.SaveAs(strFilePath)
  End Sub
End Class

When the test page runs, test the links for adding/clearing. Also, try setting the InputBoxes property, and play around with the IRepeatInfoUser properties to change the layout. Lastly, when you upload files, it will render the name of the file in the page.