This article was written for VB5/VB6 or ASP programmers want to explore server-side ActiveX ASP components and may be looking for a “how to” code demonstration of uploading files from an Internet browser.
I have more requests for information regarding uploading files from a browser than any other asp-related question. In this article, I reveal the essential code it takes to write a VB asp component that will upload files through RFC1867-capable browsers (Netscape and Explorer 3.02A +). The code is short and simple (e.g., down and dirty) and not meant to replace professionally published uploading programs used for industrial-strength situations like:
On the other hand, it may surprise you how easy the code is to write and understand. The major operations of the code is the placement of HTTP header information into a string, parsing a file name from this string, and then saving the binary file data to a disk file under the parsed file name. You’ll need a basic understanding of Visual Basic (VB), asp, and HTML form tags -- along with how to combine these essential ingredients into an asp ActiveX server-side component.What This Article Doesn’t Provide
When you look at the browser upload code, you’ll notice that it’s written merely to illustrate the basic operations needed to upload a file rather than for circumstances needing a lot of flexibility. As an example, the file that is sent from the browsers’ computer is saved in the directory of the component. You’ll have to write your own code to save files in other directories. Also, in order to simplify the code, the file-input element will be the only element sent by the form. You’ll need to dig into more advanced coding for further flexibility in this area. Multiple file downloads is not explored.
I have made the source code of my freeware component, Ezsite Upload Lite, available if you would like to enhance this article's example code beyond the mere essentials. Ezsite Upload Lite offers a number of options for changing directories, limiting the size of the downloadable file, and including or excluding downloadable files based on file extensions. Those interested can download the component and source code from my Web site: http://www.dougdean.com.
Needed: Two Internet Files (htm & asp)
We will use two text files to upload a computer file via a browser. The first is a htm file containing the code that displays a browser form (although this could be an asp file too). This browser form is used for selecting a file to send from the computer running the browser to the computer running your upload program -- all via the Web. This form differentiates itself from other browser forms by displaying a Browse button next to a text-input field. The user needs only to enter a file name in the text field or browse through their directories to select a file to send.
The second file is an asp file that will host the component containing the VB code we will write. This code will parse the HTTP header for the file’s name and receive the file’s data sent from the browser. It will then save the file’s data to the server computer.
The Sending htm File
The first htm file will need to reference the name of the second asp file since it will be sending the HTTP header, which includes the file’s data, to this second asp file. This is accomplished by placing the asp file’s URL path and name in the htm file’s FORM tag.
We'll place both our Internet files in the same directory and forgo using an absolute URL or path. In our example, the asp file receiving the data will be named “UploadReceive.asp.” This name is placed in the sending htm forms’ ACTION parameter, e.g., ACTION=" UploadReceive.asp".
The trick to making a form “transform” from an ordinary one to a ”file-sending” form is by including the ENCTYPE=”multipart/form-data” argument within the FORM tag. This, along with the METHOD=”POST” argument, is all there is to get a form tag ready to send a computer file to our asp file.
Here’s the htm code in its entirety. We’ll save it under the name “UploadSend.htm.” Again, the second asp file that we will examine next, and which will receive the data sent by this form, is named “UploadReceive.asp.”
Since we want the browser to send a file rather than a line of text, the form’s input tag will indicate its type as “FILE.” The name we include within the input tag (UploadFormName) will be associated with the name of the selected file. They will be sent within the HTTP header as a delimitated element (more on this later).
The Receiving ASP File
The UploadReceive.asp file, which hosts our component, will do nothing more than that. It justifies its existence solely by being the means to create our component. Its code is also short and sweet.
UploadReceive.asp
<HTML><HEAD><TITLE>UploadReceive example file</TITLE></HEAD><BODY>
<%
Set ObjUpload = Server.CreateObject("UploadProject.UploadClass")
ObjUpload.DoUpload
%>
</BODY></HTML>
The directory in which you save both these files must have the appropriate asp permissions.
To recap, a user links to the UploadSend.htm file which displays a browser form. This form allows the user to select a file from their computer. The computer file, along with the file’s name and a few other tidbits, is sent to the UploadReceive.asp file. The UploadReceive.asp file creates our VB object which will then do the nitty-gritty work of saving the sent file.
Setting the Context
If you're unfamiliar with developing ActiveX server-side asp components, you may want to read my article (Creating a Server Component with Visual Basic") that explains the basics of this type of programming. If things don't make sense or you're confused by a reference I make, read the article on creating a server component. Otherwise, dive in by starting a new ActiveX DLL project in either VB5 or VB6:
Start an ActiveX DLL project.
Select Project/References from the menu.
Select "Microsoft Active Server Pages Object Library" from the available references.
Click the OK button.
We’ll rename our project "UploadProject" while naming the class we’ll be writing "UploadClass."
As with any component that uses the asp-scripting context, we must declare the scripting context objects we are going to use in our project. We'll capture the objects in the OnStartPage procedure. Since we are only going to need the Request asp method, this is the only context object we will make available in our code.
Here’s the code that sets the context for the upload procedure we will be writing.
Option Explicit
'----- DECLARE ASP OBJECTS
Private MyScriptingContext As ScriptingContext
Private MyRequest As Request
Public Sub OnStartPage(PassedScriptingContext As ScriptingContext)
'----- CREATE ASP OBJECTS
Set MyScriptingContext = PassedScriptingContext
Set MyRequest = MyScriptingContext.Request
End Sub
With this small bit of code established, we can use the MyRequest object variable as we would use any asp Request object within an asp file. Of coarse, in our case we’re merely using an asp file (UploadReceive.asp) to host our ActiveX component. Thus, in order to use any asp object and its properties in our code, we must pass that asp object to our component via the code above.
We will be using only two asp Request methods -- TotalBytes and BinaryRead.
BinaryRead’s Catch 22
There is a limitation imposed on our VB code when using the asp Response method BinaryRead. This method makes the HTTP header, and our sent file available as raw data. We will need to parse this data in order to save the file sent to us, since it’s not accessible in any other way to VB.
The limitation we face is the inability to use the Request.Form method after using the Request.BinaryRead method. And we need the BinaryRead method to save the file data sent our way. What’s more, the Request.BinaryRead method can’t be used after using the Request.Form method.
In other words, we can only get our file data with BinaryRead. But by using BinaryRead we can no longer use the asp Request.Form method to retrieve the file name of the file sent to us. And if we use the Request.Form method to get the file name first, we can no longer use the Request.BinaryRead method to get the file data.
Parsing the Raw Data
What we need to do is parse out the file name from the HTTP header data so we can save the file’s data under its rightful name. This isn’t as difficult as it may seem, although we will be taking a shortcut.
An HTTP header sent from a “multipart/form-data” form includes delimitated sections containing the form’s elements and any file data sent within an element. By using the UploadSend.htm as it is structured in this article, we can guarantee that the first delimitated section of our HTTP header will contain the uploaded file data. We will take advantage of this and avoid the overhead it would take to search for our particular element among any additional elements (e.g., Checkboxes, Select Options, other file Inputs, etc.).
We’ll simply parse part of the first delimitated element from the HTTP header into a string, then extract the file name from this header string using a few VB InStr commands and a loop. Finding the file’s data is even simpler.
Here is an example of the raw data sent from our UploadSend.htm file’s form. It starts and ends with a delimiter. The file data sent in this example is “'This is a test file for ProjectUpload.” It is a simple text file I created with Notepad, although other file types (exe, gif, doc, etc.) will work as well. Its path and file name are C:\Directory\UploadFile.txt.
----------------------------7ce3023980c
Content-Disposition: form-data; name="UploadFormName"; filename="C:\Directory\UploadFile.txt"
Content-Type: text/plain
This is a test file for ProjectUpload.
-----------------------------7ce3023980c--
We’ll use the delimiter used to separate elements in the HTTP header to calculate where the end position of our file’s data is located. The Request.TotalBytes method will also help us in this endeavor. Finding the beginning position of the file’s data sent to us is as simple as finding two control-linefeeds.
Creating the DoUpload Sub
For this demonstration program, we will contain all our code within one method -- the DoUpload sub. Create the method as a Public sub.
Public Sub DoUpload()
No arguments will be passed since we will be getting all our data from the scripting context.
Define the following variables at the start of the DoUpload sub.
'~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Dim varByteCount
Dim binArray() As Byte
Dim lngFileDataStart As Long
Dim lngFileDataEnd As Long
Dim strHeadData As String
Dim intFileTagStart As Integer
Dim strPathName As String
Dim intPathNameStart As String
Dim strFileName As String
Dim intFileNameStart As Integer
Dim intFileNameEnd As Integer
Dim strDelimeter As String
Dim intCount As Integer
Dim lngCount As Long
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Each variable will be explained as we develop our component.
Retrieving the Raw Data
The first thing we’re going to do is find out how many bytes were sent to us from the browser. The TotalBytes property is a read-only property specifying the total number of bytes sent by the request. We’ll assign the TotalBytes property to a variant variable (varByteCount).
'~~~~~ BYTE COUNT OF RAW FORM DATA ~~~~~~~~~~~~
varByteCount = MyRequest.TotalBytes
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now that we have our byte count safely stored away within a variant variable, we can gather up the raw HTTP data. We’ll need to place our data within an array of type Byte. The BinaryRead method stores any retrieved data within a SafeArray. A SafeArray is an array that contains information about the number of dimensions and the bounds of its dimensions. We will put this data in an array of unsigned bytes returned by this method. Here’s how . . .
'~~~~~ PLACE RAW DATA INTO BYTE ARRAY ~~~~~~~~~
ReDim binArray(varByteCount)
binArray = MyRequest.BinaryRead(varByteCount)
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Notice that we used our varByteCount variable, containing the total amount of bytes retrieved, to dimension our array and then again to tell the BinaryRead method how many bytes to produce.
We now have the raw data tucked away into an array of bytes. We’ll use this array to extract the part of the HTTP header that contains the file name of the file being sent by the browser. It will take a little string parsing to get our file’s name out. Not surprisingly, this will take more lines of code than the actual code needed for parsing and saving the file’s data.
Parsing the HTTP Header
Now let’s parse out the part of the HTTP header containing the file’s name and place this part of the header into a string array for some further string parsing.
'~~~~~ PARSE HEADER DATA OF FIRST ELEMENT FROM BYTE ARRAY ~~~~~
intCount = 0 'binArray is base zero
Do Until Right(strHeadData, 4) = vbCrLf & vbCrLf
strHeadData = strHeadData & Chr(binArray(intCount))
intCount = intCount + 1
Loop
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The HTTP header starts at the beginning of our array of bytes. The Do Until code loops through the byte array until it detects the two control-linefeeds at the end of the element’s name/value parameters. The two control-linefeeds not only mark the end of these parameters, but the beginning of the file’s data. Stopping the loop here prevents us from needlessly gathering the file’s data into the string –remember . . . we’re keep this example simple!
Each loop adds a byte onto the string (strHeadData) after it is cast into a character type. At the end of the loop we have our element’s name/value pairs stored away in the strHeadData string variable.
It is this operation that you may want to experiment with if you have ambitions of developing a component that will upload multiple files or not assume a specific order of form elements. You’ll need a good understanding of the delimitated structure of the HTTP header. To make matters easier, I’ve provided the source code for a small component at my site ( http://www.dougdean.com ) that will display a sent HTTP header in either binary or ASCII.
Parsing the File Name
Parsing the file’s name will be done in several steps.
The input tag element’s name/value pair was sent to our asp file by the UploadSend.htm file. HTTP neatly packs a browser’s form information into name/value pairs and sends them in the HTTP header when a POST is indicated within the FORM tag.
Our input tag had a “TYPE” designated as “FILE”, and we named it “UploadFormName”:
<INPUT TYPE=FILE NAME="UploadFormName">
When using an input tag of type “FILE,” a file name/value pair will also be sent within the HTTP header. The value is the path and name of the uploaded file found on the sending computer. I uploaded a file named “UploadFile.txt” in the C:\Directory\ path. Here's an example of what you would see within the HTTP header given the above input tag and path/file name:
Since we determined the value (UploadFormName) of the name of the input tag, we can use it as a known reference to get to the file name value (UploadFile.txt) being uploaded. This information is now within our strHeadData string. We’ll start by placing the beginning string position of the “UploadFormName” into an integer variable (intFileTagStart). It’s from this position that we will launch another search for the file name value. We really don’t need the element name as a reference to search since we know it’s the one and only element sent to us. But I wanted to write code that could be extended to included mutilpal elements.
Now we are strategically positioned to search for the value of the file name/value pair we’re interested in. We will find the “filename=” half of the pair and then increase the pointer by 10 characters. This will place us at the beginning of our pathname.
To find the ending character of the path name we can again use the intFileTagStart as a reference and search for the first control-linefeed which marks the end of the path name. By then subtracting one we arrive at the position of the quote mark at the end of our path name.
This is a good place in code to check for when a user submits a form without choosing a file name. For our example code, we’ll just exit the sub if no file was sent. If both the beginning and ending positions are equal, no file name exists.
If intPathNameStart = intFileNameEnd Then Exit Sub
Now that we have both the beginning and ending positions we want, and know that a file has been sent, its simple a matter of parsing the path name and placing it in a string variable (strPathName).
Since we have our path name, but need the file name to save the file under, we’ll parse the file name from the path name. First we need to find the beginning position of the file name. Looping backwards through the path name to the first directory will do the trick. Here we’ll look for the first ‘\’ and then exit our loop with the position stored in an integer variable (intFileNameStart).
For intCount = intFileNameEnd To intPathNameStart Step -1
If Mid(strHeadData, intCount, 1) = "\" Then
intFileNameStart = intCount + 1
Exit For
End If
Next
Now with some simple subtraction we have our file name.
We’ll parse the delimiter in the HTTP header in order to find the end position of our file’s data. The HTTP delimiter [----------------------------7ce3023980c] is created anew each time a HTTP header is sent and may be different for each and every header. The delimiter is always located at the start of the HTTP header and always ends with a control-linefeed. The very last delimiter, marking the end of the HTTP header, adds two extra “--“ at the end of itself and before the last control-linefeed.
Parsing the HTTP delimiter takes just one line of VB code:
Within this line of code we first obtain the position of the character one position to the left of the first control-linefeed, e.g., InStr(strHeadData, vbCrLf) – 1. This control-linefeed marks the end of the first delimiter.
This InStr position is then used to glean the characters to the left of the control-linefeed, placing the delimiter in the string variable strDelimiter.
Locating the File Data
Finding the beginning and ending position of our file’s data is a short two-step process in our example component since we simplified our code by requiring the UploadSend.htm file to have a particular tag arrangement. Knowing that only one element was sent, we can use this arrangement to easily find the end of our file’s data.
The position marking the beginning our our file’s data is calculated by using the tag element’s reference (intFileTagStart). From here we search for the first two control-linefeeds which mark the beginning of the file’s data. We’ll need to increase our position by four to take into account the length the control-linefeeds.
'~~~~~ START AND END OF THE UPLOAD FILE DATA ~~~~~~~~
lngFileDataStart = InStr(intFileTagStart, strHeadData, vbCrLf & vbCrLf) + 4
lngFileDataEnd = CLng(varByteCount) - (Len(strDelimeter) + 6)
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Here’s were we’ll take advantage of the fact that the UploadSend.htm file sent us only one element and optimize our code a bit or two. Rather than taking the time to find the end of our file’s data by starting a search from lngFileDataStart to the next control-linefeed/delimiter, we’ll take another shortcut. The last byte within our example programs’ byte array is not the end of the file’s data but the HTTP delimiter with some extra byte padding. By subtracting the length of the delimiter (plus an extra 6 bytes) from the length of the entire byte array we can determine the end of our file’s data. I’ll leave it to you to write an EndOfFilesData function that will search for the end of an element’s file data within a multiple element HTTP header.
Saving the File Data
Finally we arrive at our destination -- saving the file’s data. Armed with the data's starting and ending positions within the byte array, we loop though the array while saving each byte under the file name we parsed from the HTTP header. You’ll want to add error trapping if you adapt this example code.
'~~~~~ SAVE THE FILE DATA ~~~~~~~~~~~~~~~~~~~~~~~~~~
Open App.Path & "\" & strFileName For Binary Access Write As #1
For lngCount = lngFileDataStart To lngFileDataEnd
Put #1, , (binArray(lngCount))
Next
Close #1
'~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Have Fun
Play around with adding class properties to determine which directory to save your file in. I enjoyed adding conditions on file extensions, notification of successful downloads, database error trapping, and limiting the acceptable file sizes through setting properties in the host asp file. You can download the source code to this freeware program, Ezsite UpLoad Lite, at my site.
EZsite UpLoad, with a multiuser password-protected management system, is also available at a nominal price.
Doug Dean is both an educational psychologist and independent programmer in southern California. His first commercial program, written in 8080 assemble language, was released in 1985.. “Please Understand Me – The Computer Version,” a personality-profiling program, continues to sell and is published by Cambridge Software. Last year, “Deluxe Edition Personality Test” was released by Virtual Entertainment and sells nationally in computer and retail outlet stores. An excerpt from “Deluxe Edition Personality Test” can be viewed at http://www.dougdean.com/learningstyles/index.cfm
Doug’s current interests lie with developing dynamic Web sites, particularly with server-side ActiveX components. He is the author of a number of components, all with remote managing capabilities and multiuser password-protected systems. EZsite Calendar, EZsite WebNotes, EZsite Forum, and EZsite UpLoad are all available from his Web site at http://www.dougdean.com.
AspUpload is an Active Server component which enables an ASP application to accept, save and manipulate files uploaded with a browser. The files are uploaded via an HTML POST form using RFC 1867. AspUpload can then manipulate the uploaded files in a number of ways which include ACL manipulation, attribute changes, saving to a database, and ActiveX DLL registration.
Uploading files is as simple as ABC with ABCUpload. Our Pure HTML Progress Bar allows your visitors to see the
progress of their upload in real time with absolutely no client side software. We also offer a number of other advanced technical features including Unicode Compliant, 120% MacBinary Compatible, BLOB Aware, support for foreign language uploads.
ABCUpload also supports COM+ and is also available in a .NET version.
With ActiveFile's advanced features, such as restart of interrupted downloads, download failure detection, and industry standard data compression, it's no wonder that companies like Associated Press and Xerox are Infomentum OEM partners. ActiveFile is the professional’s choice for leading edge capabilities that can’t be found in any other file component.
If you are looking for an intelligent way to exchange files between your ASP or ASP.NET application and web clients, the search is over. Compliant with RFC 1867, ActiveFile provides both file upload and download capabilities that work seamlessly with all of the leading web browsers. Using Active Server Pages or ASP.NET scripting, your application can manipulate files and directories using a robust set of objects and methods provided by the ActiveFile component.
An all-in-one shopping cart system that provides a universal hook to all shopping pages on your Web site, regardless what you sell. Runs for all IIS based Web servers under Windows 95, 98, NT 4.0, NT 2000. Works with all versions of FrontPage.
Microsoft Posting Acceptor allows Microsoft Internet Information Server (IIS) to accept Web content posts (files) from Microsoft Web Publishing Wizard or other clients using the RFC1867 multi-form/posting method through an http connection. Microsoft Posting Acceptor can also accept content posts from Internet Explorer 3.0 (with the ActiveX Upload control provided with Microsoft Posting Acceptor) and Netscape Navigator 2.02 or greater through forms.
FileUp is the standard in transactional file uploads and secure downloads.
Allows dynamic applications to accept, save, and manipulate files uploaded
via a browser. Files can be of any format such as Word documents, images, or
plain text. FileUp is the most comprehensive transactional file transfer
controls: supports foreign character sets; guarantees synchronization
between upload processing and database transactions; full COM+ integration;
simplified error handling; guaranteed integrity when uploading multiple
files; server-side progress indicator; performance monitor counters;
MacBinary decoding, recursive directory uploads and more! Besides the most
complete feature set, FileUp includes documentation, a large set of sample
ASP pages, and an extensive tutorial. .NET, ASP & VB
Allows working with safearray binary data. Enables binary file
upload to the ASP and download from ASP with on-fly compression or
generation binary data (Using Response.BinaryWrite and
Request.BinaryRead). Enables calling some of Kernel and Advapi functions
and work with processes and threads.
Tool Parts provide an interface for Web Part properties well beyond the capabilities of the default property pane. In this article Gayan Peiris shows how to customize Web Parts with custom Tool Parts. [Read This Article][Top]
This article demonstrates how to create a reusable component in ASP.NET 2.0 and then consume it from an ASP.NET page. Also learn how the ObjectDataSource control can be used to directly bind the output of an object to the controls in an ASP.NET page and how precompilation can be used to increase the performance of the Web application and catch compilation errors. [Read This Article][Top]
Browser Helper Objects (BHOs) are COM components that communicate with Internet Explorer to enrich the browsing experience. Michele Leroux Bustamante returns to the world of COM to show you how to build a managed BHO with the help of the .NET Framework's COM interoperability features. [Read This Article][Top]
In addition to creating custom Web Parts for SharePoint Portal Server, developers can actually create their own custom properties to further enhance Web Part appearance and behavior. Gayan Peiris explains the process and provides all the necessary code. [Read This Article][Top]
Accessing shared resources is a challenge for many ASP.NET developers. Tony Arslan explains how a simple serviced component can solve this infamous problem. [Read This Article][Top]
Using callbacks and function pointers in VB can be risky and complicated. Ben Garcia explains his work-around for the function pointer issue he encountered while creating the VB version of his SNMP component. [Read This Article][Top]
In part two of this intriguing article series, Ben Garcia shows how to build an updated and improved SNMP component in VC++ AND VB, and he briefly explains why limitations in VB make VC++ a better language for developing this type of application. [Read This Article][Top]
Ben Garcia sheds some light on the Simple Network Management Protocol
(SNMP). First he provides a history of SNMP, then he dives right into its
architecture. Finally, he shows how to build a COM component that
communicates with SNMP-enabled devices. [Read This Article][Top]
Paul Apostolos begins his series on using Web services and the MSComm32.OCX
component to access caller id information from a Web page. In part 1, learn how to write the Visual Basic program that runs on the server and updates a database with incoming callers.
[Read This Article][Top]
Doug Dean explains different methods of retrieving and manipulating data from a database in a VB DLL so that it is ready to be rendered in a browser. [Read This Article][Top]
Mailing List
Want to receive email when the next article is published? Just Click Here to sign up.