Professional Documents
Culture Documents
Uploading
Uploading
uk/show/1615/
Hosting Limitations
You should be warned that, before we start, not all hosting packages allow you to upload your own ActiveX
components. If this is the case, then unfortunately you will have to revert to a pure ASP script.
Getting Started
Before we begin, create a new folder called ' uploaddemo' in your C:\inetpub\wwwroot directory (or where-ever else your
files are stored for IIS). Create a new file in there called upload.asp, and another called uploadcomplete.asp. Leave
these files empty for now.
Now, create another directory within 'uploaddemo' called 'files'. This is where our uploaded files are going to be stored.
In order for this to work, you need to allow IIS to be given write access to this directory. To do this, go to the Internet
Information Services console, select the appropriate web, and then display the properties for the 'files' directory. In the
Directory tab, ensure the 'Write' checkbox is selected, and click OK.
We're now ready to write some code for upload.asp, where the user will be presented with a form to select a file, and
give the file a description. Add the following code to the upload.asp file, and then we'll take a closer look at it.
<html>
<head>
<title>VB Web ASP File Upload</title>
</head>
<body>
<form action="uploadcomplete.asp" method="POST" enctype="multipart/form-data">
<p>Please select a file to upload, and enter a description for it.</p>
<p><b>File: </b><input type="file" style="width: 300;" name="thefile"><br>
<b>Description:</b><br><textarea name="description" rows=4 cols=30></textarea></p>
<input type="submit" value="Upload">
</form>
</body>
</html>
As you can see, the form is currently just plain HTML. However, there are a few differences that you may not have come
across before. The first is this:
Although at first glance this is a standard form, the enctype property makes a significant difference! When set to
multipart/form-data, the form is not posted in the standard format - you will not be able to access the posted data
through the standard Request.Form(FieldName) syntax. However, this format does allow files to be transmitted to the
server, as well as form fields. When we create our ActiveX DLL, it will parse this posted data, and save the appropriate
part into a new file.
line, which uses the "file" input type. This input field allows the user to select a file using an Open dialog.
The VB Component
Now, we can get started on the VB portion of this tutorial. Create a new ActiveX DLL project in VB, and set the Project's
name to 'ASPUploadComponent'. Next, create two Classes to the project, and call them cUpload and cFormItem
respectively. cUpload's Instancing property should be set to 5 - MultiUse, and cFormItem's should be set to 2 -
PublicNotCreatable.
Finally, you need to add a reference to the ASP Type Library. To do this, go to Project|References, and check the box
next to Microsoft Active Server Pages Object Library, and the Microsoft Scripting Runtime (so we can use the
FileSystemObject). Now we can get coding...!
Therefore, your only option is to compile your component each time you make a change. However, it gets worse...
When your script accesses the component for the first time, it locks the file. Your OCX file is not unlocked again until
the IIS service stops, meaning that each time you want VB to re-compile the component you have to restart IIS (and
not just the web you are using either... all of IIS!). To restart IIS, go to Services, select World Wide Publishing Service,
and click Restart.
One last thing... if you would prefer to skip the VB coding section, you can download the source code and binaries here.
Then, you can skip to this step.
First, I'll list all the code that is needed for the component, and then we can examine it more closely. Add the following
code to cUpload.
Option Explicit
'*****************************************************************
' Purpose: Retrieve a file by HTTP protocol and writes this file
' to a location on the webserver
'*****************************************************************
'Error Definitions
Private Const ERR_FIELD_DOES_NOT_EXIST As Long = vbObjectError + 102
'*****************************************************************
' OnStartPage()
' Purpose: Capture of Active Server Pages objects
'*****************************************************************
Public Sub OnStartPage(PassedScriptingContext As ScriptingContext)
Set MyScriptingContext = PassedScriptingContext
Set MyRequest = MyScriptingContext.Request
'*****************************************************************
' Item()
' Purpose: Return a form item
' Inputs: vKey -- the key or index of the item to return
' Returns: A cFormItem object
'*****************************************************************
Public Property Get Form(Key As Variant) As cFormItem
If FieldExists(Key) = False Then
Err.Raise ERR_FIELD_DOES_NOT_EXIST, "UploadIt", "The specified item does not exist"
Else
Set Form = cFormItems(Key)
End If
End Property
'*****************************************************************
' Count()
' Purpose: Gets the number of form fields
' Returns: The number of form fields
'*****************************************************************
Public Property Get Count() As Long
Count = cFormItems.Count
End Property
'*****************************************************************
' BuildForm()
' Purpose: Parse the posted form.
' Returns: A collection containing all the forms fields.
'*****************************************************************
Private Function BuildForm() As Collection
'Variables
Dim varByteCount As Variant 'the number of bytes sent
Dim varHTTPHeader As Variant 'the HTTP header
Dim varDelimeter As Variant 'the delimiter that divides the fields
Dim varFields As Collection 'contains all the form fields
'Initialize collection
Set varFields = New Collection
'Delimeter string
varDelimeter = LeftB(varHTTPHeader, 76)
'Filter formfieldvalue
strFormFieldValue = MidB(varHTTPHeader, lngFormFieldValueStart, lngFormFieldValueEnd -
lngFormFieldValueStart)
NextItem:
BuildForm_ExitProc:
'Return an array with the formfield names and values
Set BuildForm = varFields
Exit Function
BuildForm_Err:
Err.Raise Err, "ASPUploadComponent", "Unhandled error: " & Error
End Function
'*****************************************************************
' ItemExists()
' Purpose: Determines if a field exists.
' Inputs: strKey -- A string containing the key to search for
' Returns: Whether the entry exists or not
'*****************************************************************
Public Function FieldExists(ByVal Key As Variant) As Boolean
Dim cItem As cFormItem
On Error Resume Next
Set cItem = cFormItems(Key)
If Err = 0 Then FieldExists = True
End Function
'*****************************************************************
' GetFileName()
' Purpose: Parses the filename from the filepath.
' Inputs: strFilePath -- String containing filepath and filename
' Returns: A string which contains the filename
'*****************************************************************
Private Function GetFileName(strFilePath) As String
GetFileName = strFilePath
For intPos = Len(strFilePath) To 1 Step -1
If Mid(strFilePath, intPos, 1) = "\" Or Mid(strFilePath, intPos, 1) = ":" Then
GetFileName = Right(strFilePath, Len(strFilePath) - intPos)
Exit Function
End If
Next
End Function
Source Code (2)
Now add the following code to cFormItem. This makes up a form field object.
Option Explicit
'Error Definitions
Private Const ERR_FILESIZE_NOT_ALLOWED As Long = vbObjectError + 103
Private Const ERR_FOLDER_DOES_NOT_EXIST As Long = vbObjectError + 104
Private Const ERR_FILE_ALREADY_EXISTS As Long = vbObjectError + 105
Private Const ERR_FILE_TYPE_NOT_ALLOWED As Long = vbObjectError + 106
'*****************************************************************
' Add()
' Purpose: Adds an item to the field collection
' Inputs: strKey -- the item's key
' strValue -- string containing the value
'*****************************************************************
Friend Sub Add(ByVal strKey As String, ByVal strValue As String)
cData.Add strValue, strKey
End Sub
'*****************************************************************
' FileData()
' Purpose: Sets the contents of a file
' Inputs: varContent -- the content of the file
'*****************************************************************
'*****************************************************************
' Properties
' Purpose: Returns various public values
'*****************************************************************
Public Property Get Value() As String
Value = GetVal("FileName")
End Property
Public Property Get FileSize() As String
FileSize = GetVal("FileLen")
End Property
Public Property Get Name() As String
Name = GetVal("Name")
End Property
Public Property Get ContentType() As String
ContentType = GetVal("ContentType")
End Property
Public Property Get IsFile() As Boolean
IsFile = Exists("FileName")
End Property
'*****************************************************************
' Private Functions
'*****************************************************************
'*****************************************************************
' SaveFile()
' Purpose: Saves the form entry to a file (if it is one)
' Inputs: strUploadPath -- string containing the directory to upload to
' strFileName -- string containing the filename
' strExcludeFileExtensions -- file extensions to exclude
'*****************************************************************
Public Sub SaveFile(ByVal strUploadPath As String, ByVal strFileName As String, Optional ByVal
strExcludeFileExtensions As String = "", Optional ByVal lMaxByteCount As Long = 0)
'we can only save files...
If IsFile = False Then Exit Sub
If strFileName = "" Then strFileName = cData("FileName")
'Check to see if file extensions are excluded
If strExcludeFileExtensions <> "" Then
If ValidFileExtension(strFileName, strExcludeFileExtensions) Then
Err.Raise ERR_FILE_TYPE_NOT_ALLOWED, "ASPUploadComponent", "It is not allowed to upload a file
containing a [." & GetFileExtension(strFileName) & "] extension"
End If
End If
'*****************************************************************
' WriteFile()
' Purpose: Writes the uploaded file to a given directory
' Inputs: strUploadPath -- string containing the directory to upload to
' strFileName -- string containing the filename
'*****************************************************************
Private Sub WriteFile(ByVal strUploadPath As String, ByVal strFileName As String)
'Create file
Set sFile = fs.CreateTextFile(strUploadPath & strFileName, True)
'Write file
sFile.Write varFileData
'Close File
sFile.Close
Exit Sub
WriteFile_Err:
Err.Raise Err.Number
End Sub
'*****************************************************************
' ValidFileExtension()
' Purpose: Checks if the file extension is allowed
' Inputs: strFileName -- the filename
' strFileExtension -- the fileextensions not allowed
' Returns: boolean
'*****************************************************************
strFileExtension = UCase(GetFileExtension(strFileName))
For i = 0 To UBound(arrExtension)
Next
ValidFileExtension = False
End Function
'*****************************************************************
' GetFileExtension()
' Purpose: Returns the extension of a filename
' Inputs: strFileName -- string containing the filename
' varContent -- variant containing the filedata
' Outputs: a string containing the fileextension
'*****************************************************************
End Function
cUpload: A closer look
Lets take a closer look at this cUpload class first. After a few public variables for the class, you'll notice the
line, which doesn't appear to be called by anything. In fact, this procedure is called by the Active Server Pages engine
when the object is initialized in our ASP script, and passes us a ScriptingContext object. This object contains all the
objects available to your ASP script; Response, Request, Server etc. In this instance, we are only interested in the
Request object (what has been sent to the ASP script), so we save it to our MyRequest object using
We then proceed to call the BuildForm procedure, which parses the data sent to us via the form on upload.asp. This is
where the bulk of our code lies.
As the form was posted using the encoding method, we cannot access the posted objects using the standard
Request.Form object. Instead, we read the entire HTTP header into a variable. First, we get the length of the header:
varByteCount = MyRequest.TotalBytes
You will notice that we have also converted the header into Unicode. This is to make it easier for us to parse the posted
data; instead of receiving binary data, we receive at least some readable characters. Below is a sample of what might
now be present in varHTTPHeader.
-----------------------------7d130d3810678
Content-Disposition: form-data; name="thefile"; filename="C:\devpad_description.txt"
Content-Type: text/plain
Developers Pad - the ultimate programming editor. Fully configurable, with syntax highlighting, auto-indent,
code library, powerful project view, quick-tag, find & replace, find in files, add-ins and more!
-----------------------------7d130d3810678
Content-Disposition: form-data; name="filedescription"
-----------------------------7d130d3810678
Content-Disposition: form-data; name="folder"
0
-----------------------------7d130d3810678--
As you can see, each field is seperated by -----------------------------7d130d3810678. Next, we are told the we are
type of content, and the field name. In the case of the file, we are also given the filename and content type. Finally, we
are given the value of that field. The BuildForm procedure now parses this data, and creates the correct number of form
objects. As the delimiter may vary, we check it by taking the first 76 bytes:
Note we use the InStrB function so that we are returned the byte position rather than character position. Now, we start
a Do...Loop which continues until lngFormFieldNameStart = 0. For each field, we first initialize the FormField object:
Next, we find out where the name property is going to end. We do this by searching for a " character (Chr(34)), from the
fields starting position, plus the length of ; name=". Now that we have the start and end positions, we can retreive the
name of the field:
Now we check to see if there is a ; after the name field... If there is, we know it is a file, otherwise, it isn't.
If it is a file, we proceed to get the filename: and ContentType properties. After the ContentType property, we search
for two vbCrLf (new lines), after which is the file data. We now save all this extra file data (filename, file size, content
type etc) to the clsFormField object.
If the form field isn't a file, we perform a similar operation, except we skip searching for the filename, and just get the
form fields value instead. Finally, we save the fields name, and add the clsFormField object to our varFields collection:
We then reset the clsFormField, and search for another field to process.... and this continues until we have parsed all
the fields. We then return the varFields collection for use by our ASP script.
Phew... that was an awful lot of code to get through. Obviously, we haven't covered every line, but hopefully by now
you should get the idea. The clsFormField object is basically used to store information about that field. A standard field
will simply have it's name and value recorded. Meanwhile, a file also has a FileSize, ContentType and FileName property.
Once parsed, our ASP script can then access all this information, and use the cFormField object's SaveFile method to
save each file to the server's hard disk. This will be demonstrated on the next page.
First, however, we need to compile our component to a DLL file. To do this, go to File | Make
ASPUploadComponent.dll . Before it can be used in your ASP script, however, IUSR_COMPUTERNAMEneeds to be given
permission to access the file (and lock it!). To do this, display the file properties from explorer, and give
IUSR_COMPUTERNAME read/execute permissions. Now, we are ready to create our other asp page.
Now that we have created the VB ActiveX DLL component, we can code the uploadcomplete.asp file. Add the following
code to uploadcomplete.asp, and we'll take a look at what exactly is going on.
<%
Option Explicit
Dim objUpload, strUploadPath, strMsg, strFileExtensions
So, lets have a quick look at how this code works. First, we create an instance of the ActiveX DLL using
(as we can't add a reference to the DLL like we would in Visual Basic). The next two lines set the path on the server
where the file is going to be uploaded, and we set strFileExtensions, specifying the file extensions to exclude. (This is
always a good idea, as you don't want your users uploading executables to your server!).
Next, we check to see if the field, 'thefile', exists, using the FieldExists property exposed by the objUpload object:
If it doesn't, we know the form has not been posted correctly, or the user has not selected a file. If it does exist, we try
to save the file:
If an error occurs, we trap the error, and otherwise, tell the user the file has been uploaded. At this stage, we could
also add the description field to a database, for example. The rest of the page outputs the HTML, and displays the
information exposed about the file that was uploaded...
One final "how-to"... Unfortunately, the file HTML input tag doesn't support selecting multiple files. However, you can
have more than one file field in your form, with a different name. Therefore, you can upload multiple files using the
component we built using
etc...
And there you have it... an fully featured upload facility and re-usable ActiveX component. Easy, wasn't it.... hmmm...
maybe it will improve in ASP.NET!
James first started writing tutorials on Visual Basic in 1999 whilst starting this website (then known as VB Web). Since then,
the site has grown rapidly, and James has written numerous tutorials, articles and reviews on VB, PHP, ASP and C#. In
October 2003, James formed the company Developer Fusion Ltd, which owns this website, and also offers various
development services. In his spare time, he's a 3rd year undergraduate studying Computer Science in the UK. He's also a
Visual Basic MVP.
Related Content
Using the SAFileup component
ASP Upload Component
Beginning Active Server Pages