Programmer to ProgrammerTM  
Wrox Press Ltd  
   
  Search ASPToday Living Book ASPToday Living Book
Index Full Text
  SoftArtisans
ASPToday Home
 
 
Home HOME
Site Map SITE MAP
Index INDEX
Full-text search SEARCH
Forum FORUM
Feedback FEEDBACK
Advertise with us ADVERTISE
Subscribe SUBSCRIBE
Bullet LOG OFF
                         
      The ASPToday Article
March 16, 2000
      Previous article -
March 15, 2000
  Next article -
March 17, 2000
 
   
   
   
File Uploading with ASP and VBScript   Philippe Collignon  
by Philippe Collignon
 
CATEGORY:  Scripting  
ARTICLE TYPE: Overview Reader Comments
   
    ABSTRACT  
 
Article Rating
 
   Useful
  
   Innovative
  
   Informative
  
 1775 responses

Philippe Collignon presents a complete VBScript solution to uploading files. The sample code provided here is pure VBScript and is independent of third party products, that are often expensive and usually unable to be customized. Though VBScript may not be the obvious choice because it is a scripting language designed to work only with datatype variants and it does not provide many built-in functions to manage binary data and byte arrays, it does provide a very light-weight, but powerful solution to your uploading requirements.

   
                   
    Article Discussion   Rate this article   Related Links   Index Entries  
   
 
    ARTICLE

File uploading from a browser is an easy way to transfer files from client to server. Most browsers, from the third generation browsers from Netscape and Microsoft (with an add-on) and up, can upload files to server. No special access or software has to be provided to the user.

Many ASP components have been developed for file upload, including:

The beginning of this article should give you enough information to build such components, which are usually written in VB, C++ or Java.

The problem with those components is they are third party products and they are not part of standard ASP. As third party components, they have to be installed on the server. This means you have to copy the DLL on the server and register it. Most hosting organizations do not allow such setups on their servers since it produces configuration issues (especially for virtual hosting). A second disadvantage is that most of them are not free, source code is not provided and so, you cannot customize them as you want.

These are all the reasons I needed to write a VBScript code that gives a complete upload solution. It was not an obvious choice because VBScript is a scripting language designed to work only with datatype variants and it does not provide many built-in functions to manage binary data and byte arrays.

To understand the uploading process, you first need to know the way data is sent from browser to server with the HTTP protocol. That means understanding the " multipart/form-data " submit.

Upload Form

Usually, you use an HTML FORM to submit data from the browser to the web server. That form can contain text fields, checkbox, button and also a file type control to upload files. The user fills in the form with his data and submits the form to the server.

The enctype attribute of the <FORM> element specifies the content type used to encode the form data set for submission to the server. The enctype attribute used by default is " application/x-www-form-urlencoded ". However, that enctype is inefficient for sending large quantities of text, text containing non-ASCII characters or binary data to the server. The content type " multipart/form-data " should be used to submit forms in case of file uploading. Actually, it can be used to submit files and binary data.

A " multipart/form-data " message contains a series of parts, where each part is expected to contain:

For a file type control, the part contains some more information:

Those headers are followed by the binary or text content of the control.

The following example illustrates " multipart/form-data " encoding. The client would have this form in the browser:

<FORM METHOD="POST" ENCTYPE="multipart/form-data" ACTION="upload.asp">
<INPUT TYPE="Text" NAME="email" VALUE="[email protected]"><BR>
<INPUT TYPE="file" NAME="blob"><BR>
<INPUT TYPE="submit" NAME="Enter">
</FORM>

If this form is submitted, the following request can be read on the server:

-----------------------------7cf87224d2020a 
Content-Disposition: form-data; name="email" 

[email protected]
-----------------------------7cf87224d2020a
Content-Disposition: form-data; name="blob"; filename="c:\image.gif"
Content-Type: image/pjpeg 

ÿØÿàJFIFHHÿÛC…
…ÿÙ 
-----------------------------7cf87224d2020a 
Content-Disposition: form-data; name="Enter" 

Submit Query 
-----------------------------7cf87224d2020a-- 

That content can be displayed if it is sent back as a response to the client. The binary data should be read and written with Request.binaryRead and Response.binaryWrite methods.

<%
Response.BinaryWrite(Request.BinaryRead(Request.TotalBytes))
%>

You can see that parts of the response are delimited by boundaries:

-----------------------------7cf87224d2020a

and the last boundary is followed by: ' -- '

There is one Content-Disposition for each control. The name attribute identifies the control sent by the HTML form ( email , blob and Enter ). For a file type control (blob), the file name is also part of Content-Disposition header and, a Content-Type header gives the content type of the binary data.

The Upload Script

All the content presented above has to be parsed. In VB or C++, this is quite obvious because many objects and methods are provided for that. To do this in VBScript, we have to use some functions provided by the language and get round the problem of unicode variant string used in VBScript.

Vbscript Functions

The raw data is in a binary format, so we have to use VBScript function designed to manage binary data. As we can consider the raw data as a string of bytes, the MidB , InstrB and LenB functions become very useful. We have to avoid classic VBScript strings, which are unicode strings not suitable for parsing single bytes.

These are the only VBScript functions available to parse bytes. We still need a method to get a unicode string from the parsed data in order to use that string in the VBScript code. It should also be useful to have a function that converts unicode string to a bytes string in order to use that string as an argument in InstrB .

I wrote two functions for that purpose, they are called getString() and getByteString() . They will be explained later in this article.

Structure

The parsed data are stored in VBScript Dictionary objects. Dictionary objects are hash table objects that store (key, item) pairs. This object is part of VBScript and ASP2.0.

A first Dictionary object " UploadRequest " is defined. That dictionary object contains all the controls submitted by the upload form. Keys are the names of the controls and Items are themselves dictionary objects that contains information about the control:

    "ControlName1", Dictionary control1
    "ControlName2", Dictionary control2

A Dictionary object that represents a control contains the following (key, item) pairs.

    "Value", String or binary content
    "FileName", Name of uploaded file
    "ContentType", ContentType of uploaded file

Combining all, we have for example:

    UploadRequest :
        "email", UploadControl 1 :  
                "Value", [email protected]
         "blob" , UploadControl 2 :
                "filename", C:/image/file.gif
                "ContentType" : image/gif
                "Value" : GIF89ai…

This is an "object" approach quite useful to access and use data afterwards.

Parser

Here is the code to parse, read and record upload controls. The process is done by a routine "BuildUploadRequest" ; this routine takes only one argument that is the raw binary data RequestBin .

Sub BuildUploadRequest(RequestBin)

First we try to find the boundary. This boundary is useful to know when the loop on controls ends.

    'Get the boundary
    PosBeg = 1
    PosEnd = InstrB(PosBeg,RequestBin,getByteString(chr(13)))
    boundary = MidB(RequestBin,PosBeg,PosEnd-PosBeg)
    boundaryPos = InstrB(1,RequestBin,boundary)

A problem is that InstrB needs byte strings as arguments. A function was written for that purpose: getByteString(String) method can convert the unicode VBScript string to a byte string. This function is described at the end of the code explanations.

Let’s do a loop until we find the closing boundary.

    'Get all data inside the boundaries
    Do until (boundaryPos=InstrB(RequestBin,boundary & getByteString("--")))

For each step in the loop, we process one control. All data about that control are saved in a dictionary object. A new dictionary object UploadControl is created at each loop.

        'Members variable of objects are put in a dictionary object
        Dim UploadControl
        Set UploadControl = CreateObject("Scripting.Dictionary")

We first get the name of the control, which can be found in the " Content-Disposition " header. The end of the name is delimited by the " character or chr(34).

        'Get an object name
        Pos = InstrB(BoundaryPos,RequestBin,_
                     getByteString("Content-Disposition"))
        Pos = InstrB(Pos,RequestBin,getByteString("name="))
        PosBeg = Pos+6
        PosEnd = InstrB(PosBeg,RequestBin,getByteString(chr(34)))
        Name = getString(MidB(RequestBin,PosBeg,PosEnd-PosBeg))

Here we have to test if the control is a file type control or a text type control. If the control is a text type control, no other data than its name are provided. If the control is a file type control, we can get more information such as file name and Content-Type.

        PosFile=InstrB(BoundaryPos,RequestBin,getByteString("filename="))
        PosBound = InstrB(PosEnd,RequestBin,boundary)
        'Test if object is of file type
        If  PosFile<>0 AND (PosFile<PosBound) Then

If control is a file type control, we parse the path and file name and add them to the dictionary object of the control. The parsed file name is a string of bytes, it is necessary to convert it to unicode so that it can be used as a variant string variable. This is done by the getString() method defined at the end.

        'Get Filename, content-type and content of file
        PosBeg = PosFile + 10
        PosEnd = InstrB(PosBeg,RequestBin,getByteString(chr(34)))
        FileName = getString(MidB(RequestBin,PosBeg,PosEnd-PosBeg))
        'Add filename to dictionary object
        UploadControl.Add "FileName", FileName
        Pos = InstrB(PosEnd,RequestBin,getByteString("Content-Type:"))
        PosBeg = Pos+14
        PosEnd = InstrB(PosBeg,RequestBin,getByteString(chr(13)))
        'Add content-type to dictionary object
        ContentType = getString(MidB(RequestBin,PosBeg,PosEnd-PosBeg))
        UploadControl.Add "ContentType",ContentType

Now we can get the core content of the file. The content does not have to be converted because it is a binary content. It should be saved to a file system or put as binary long object (blob) in a database.

        'Get content of object
        PosBeg = PosEnd+4
        PosEnd = InstrB(PosBeg,RequestBin,boundary)-2
        Value = MidB(RequestBin,PosBeg,PosEnd-PosBeg)
    Else

In the case of a text type control, there is no other data to parse than the content. The content is converted to a unicode string so that it can be used later in the VBScript code.

        'Get content of object
        Pos = InstrB(Pos,RequestBin,getByteString(chr(13)))
        PosBeg = Pos+4
        PosEnd = InstrB(PosBeg,RequestBin,boundary)-2
        Value = getString(MidB(RequestBin,PosBeg,PosEnd-PosBeg))
    End If

The content is added to the dictionary object. The key is set to " Value ", and the item is the content. That content can be either a string or a binary data depending on the type of the control.

        'Add content to dictionary object
        UploadControl.Add "Value" , Value    

Finally, the dictionary object of the control is added to the global dictionary object. The key used is the name of the control. The item is the dictionary objet UploadControl we have just built.

        'Add dictionary object to main dictionary
        UploadRequest.Add name, UploadControl    
        'Loop to next object
        BoundaryPos=InstrB(BoundaryPos+LenB(boundary),RequestBin,boundary)
    Loop

End Sub

Byte-String Conversion Functions

Here is the function used to convert a byte string to a unicode string.

'Byte string to string conversion
Function getString(StringBin)
 getString =""
 For intCount = 1 to LenB(StringBin)
    getString = getString & chr(AscB(MidB(StringBin,intCount,1))) 
 Next
End Function

The other function I wrote was GetByteString, to convert a string to a byte string. Here it is:

'String to byte string conversion
Function getByteString(StringStr)
 For i = 1 to Len(StringStr)
 	char = Mid(StringStr,i,1)
	getByteString = getByteString & chrB(AscB(char))
 Next
End Function   

Use of Upload Script

Here are sample uses of the upload script developed here. Samples files and code are provided with the download file of this article. Extract the zip file to a directory and configure a virtual directory for your web server. You can test and launch uploadForm.html from your browser.

Calling the Script

Here is the way to call the upload BuildUploadRequest method. You have first to define the global dictionary UploadRequest . Then you have to call the BuilUploadRequest method and pass to the request raw binary data in the argument.

byteCount = Request.TotalBytes
RequestBin = Request.BinaryRead(byteCount)
Dim UploadRequest
Set UploadRequest = CreateObject("Scripting.Dictionary")

BuildUploadRequest  RequestBin

The data is parsed and saved in the dictionary object that you can retrieve with Item() method. These item data can be stored in VBScript variables and used everywhere in the code. Data can simply be sent back as response to the client, used in ASP code, written to a file or put in a database field.

Retrieving the Data

The data of the UploadRequest object can be accessed by the Item("key") function. Let’s consider a scenario where you would like to access the value of the email control; this can be done by:

    email = UploadRequest.Item("email").Item("Value")

As this is a text type control, content is a string and can be used as any other VBScript string. For binary data, content can be retrieved in the same way.

     picture = UploadRequest.Item("blob").Item("Value")
  
 

It is also possible to access other information like file name or content-type. They are text type controls.

    contentType = UploadRequest.Item("blob").Item("ContentType")
    filepathname = UploadRequest.Item("blob").Item("FileName")

Using the Data in VBScript Code

The uploaded data can be used in VBScript code as any other variable. They can, for example, be sent back as response to the client.

    Your email is : <%=email%>
    File name of you picture is <%=filepathname%>
    File type of your picture is <%=contentType%>

Binary data can also be sent back to the client. A content-type has to be set and the binary data should be written with the BinaryWrite method.

    Response.ContentType = contentType
    Response.BinaryWrite picture

Writing Uploaded Data to File

In the case of a file type control, the goal is usually to store the binary data in a file or in a database field rather than sending them back to the client. This "persistent" feature is one of the main goals of file uploading. The FileSystem object can be used to store the uploaded file to the file system of the server.

First let us create a FileSystem object:

'Create FileSytemObject Component
 Set ScriptObject = Server.CreateObject("Scripting.FileSystemObject")

Create a file in a directory with the fileSystem object. That directory can be absolute and refer directly to the file system (e.g. c:\temp ). The directory can also be relative to a virtual directory defined on the web server. This virtual directory is mapped to the absolute directory with the mappath method and the PATH_INFO server variable.

The Write method needs a unicode string as argument so we convert the byte array to a string. The Write method is responsible for converting that unicode string and writes it using ASCII format. This builds a file that contains the binary content of our original byte string. I have called that file ' uploaded+filename ', just for distinguishing the file, but you can choose whatever filename you like:

'Create and Write to a File
Set MyFile = ScriptObject.CreateTextFile(Server.mappath(Request.ServerVariables_
                              ("PATH_INFO")) & "uploaded" & filename)
 MyFile.Write getString(value)
 MyFile.Close

Storing Uploaded Data to Database

The data could also be stored in a database. The content-type should also be stored in the database to be able to display the data afterwards. You first have to establish a connection to your database, assuming you already have set up the appropriate DSN:

Set conn = Server.CreateObject("ADODB.Connection")
conn.open "DSN=wroxdns","user","pass"

Then create a Recordset from that connection:

sql = "SELECT PHOTO, CONTENTTYPE FROM MYTABLE"
Set rs = Server.CreateObject("ADODB.Recordset")
rs.Open sql, conn, 3, 3

When the Recordset is created, you have to put the binary data in the blob field of the database:

picturechunk =  picture & chrB(0)
rs.Fields("PICTURE").appendChunk picturechunk
rs.Fields("CONTENTTYPE") = contentType
rs.Update
conn.close

I had to get round a small bug in appendChunk method. In fact, I noticed that when my binary data had an odd number of bytes, the appendChunk method did not transfer the last byte! A solution was to add a chr(0) to be sure to transfer all the bytes. Maybe there is another solution for that, if so, let me know.

To get the image from the database, use the same Recordset and send it back as response to the client with the right content type.

Response.contentType = rs.Fields("CONTENTTYPE")
size = rs.Fields("PICTURE").ActualSize
blob = rs.Fields("PICTURE").GetChunk(size)
Response.binarywrite blob    

Conclusion

This article presents a complete VBScript solution to uploading files. This code is pure VBScript and is independent of third party products.

It focuses first on the upload process (HTML form submission with the content type " multipart/form-data "). Then the uploading VBScript code is explained in detail. This starts with a brief recall of VBScript functions needed to manipulate strings and byte arrays. I then presented the code of the upload script and the structure of the uploaded data.

Finally we showed several uses for this script, from simply using an uploaded variable in ASP code to the storage of an uploaded file in a database or in the file system.

 
 
   
  RATE THIS ARTICLE
  Please rate this article (1-5). Was this article...
 
 
Useful? No Yes, Very
 
Innovative? No Yes, Very
 
Informative? No Yes, Very
 
Brief Reader Comments?
Your Name:
(Optional)
 
  USEFUL LINKS
  Related Tasks:
 
 
   
  Related ASPToday Articles
   
  • Processing Received Emails On The Server (March 21, 2001)
  • Downloading a File from the Server to the Client Using ASP (February 5, 2001)
  • Building a File Upload utility with ASP.NET (December 14, 2000)
  • Create a common user updateable news publishing system with IE HTML Editing (December 13, 2000)
  • Using SOAP and the File System Object to build a text file Upload Facility (December 8, 2000)
  •  
           
     
     
      Related Sources
     
  • AspSmartUpload (Advantys): http://www.advantys.com,
  • AspUpload (PersistsSoftware): http://www.aspupload.com,
  • Posting Acceptor (part of Microsoft SiteServer): http://www.microsoft.com/siteserver
  • SA-FileUp (Software Artisants) : http://www.safileup.com
  •  
     
           
      Search the ASPToday Living Book   ASPToday Living Book
     
      Index Full Text Advanced 
     
     
           
      Index Entries in this Article
     
  • ActualSize property
  •  
  • AppendChunk method
  •  
  • ASP
  •  
  • AspSmartUpload component
  •  
  • AspUpload component
  •  
  • BinaryRead method
  •  
  • BinaryWrite method
  •  
  • BuildUploadRequest method
  •  
  • byte strings
  •  
  • code
  •  
  • components
  •  
  • components, ASP
  •  
  • Connection object
  •  
  • ContentType property
  •  
  • converting byte strings to unicode strings
  •  
  • converting to byte strings
  •  
  • converting to unicode strings
  •  
  • converting unicode strings to byte strings
  •  
  • CreateObject method
  •  
  • CreateTextFile method
  •  
  • data, retrieving
  •  
  • data, storing to database
  •  
  • data, using
  •  
  • data, writing to file
  •  
  • Dictionary object
  •  
  • enctype attribute
  •  
  • enctype attribute, Form element
  •  
  • example
  •  
  • Field object
  •  
  • file uploading
  •  
  • file uploading, VBScript
  •  
  • FileSystemObject object
  •  
  • Form element
  •  
  • getByteString function
  •  
  • GetChunk method
  •  
  • getString function
  •  
  • HTTP protocol
  •  
  • InstrB function
  •  
  • Item method
  •  
  • LenB function
  •  
  • MapPath method
  •  
  • message structure
  •  
  • MidB function
  •  
  • multipart/form-data
  •  
  • Open method
  •  
  • Posting Acceptor component
  •  
  • problems with
  •  
  • Recordset object
  •  
  • Request object
  •  
  • Response object
  •  
  • SA-FileUp component
  •  
  • sample uses
  •  
  • script, calling
  •  
  • Server object
  •  
  • TextStream object
  •  
  • TotalBytes property
  •  
  • unicode strings
  •  
  • Update method
  •  
  • upload form
  •  
  • Upload script
  •  
  • VBScript
  •  
  • Write method
  •  
     
     
    HOME | SITE MAP | INDEX | SEARCH | REFERENCE | FEEDBACK | ADVERTISE | SUBSCRIBE
    .NET Framework Components Data Access DNA 2000 E-commerce Performance
    Security Admin Site Design Scripting XML/Data Transfer Other Technologies

     
    ASPToday is brought to you by Wrox Press (www.wrox.com). Please see our terms and conditions and privacy policy.
    ASPToday is optimised for Microsoft Internet Explorer 5 browsers.
    Please report any website problems to [email protected]. Copyright © 2001 Wrox Press. All Rights Reserved.