发信人: nightcat()
整理人: qcrsoft(2002-05-13 16:33:05), 站内信件
|
File Uploading with ASP and VBScript
File uploading from a browser is an easy way to transfer files from cl ient to server. Most browsers, from the third generation browsers of N etscape and Microsoft (with an add-on) and up, can upload files to ser ver. No special access or software has to be provided to the user.
Many ASP components have been developed for file upload, including:
Posting Acceptor (part of Microsoft SiteServer),
AspSmartUpload (Advantys),
AspUpload (PersistsSoftware),
SA-FileUp (Software Artisants)
The beginning of this article should give you enough information to bu ild 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 ha ve 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 cus tomize 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 VBSc ript is a scripting language designed to work only with datatype varia nts and it does not provide many built-in functions to manage binary d ata and byte arrays.
To understand the uploading process, you first need to know the way da ta is sent from browser to server with the HTTP protocol. That means u nderstanding the " multipart/form-data " submit.
Upload Form
Usually, you use an HTML FORM to submit data from the browser to the w eb server. That form can contain text fields, checkbox, button and als o 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 en ctype 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 se rver. The content type " multipart/form-data " should be used to submi t 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 ea ch part is expected to contain:
a Content-Disposition header whose value is " form-data "
a name attribute specifying the control name.
For a file type control, the part contains some more information:
a filename attribute specifying the original path and file name on the client
a Content-Type header of the binary data control sent.
Those headers are followed by the binary or text content of the contro l.
The following example illustrates " multipart/form-data " encoding. Th e 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 se rver:
-----------------------------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�跜??�?
-----------------------------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.binary Read 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 o f the binary data.
The Upload Script
All the content presented above has to be parsed. In VB or C++, this i s quite obvious because many objects and methods are provided for that . To do this in VBScript, we have to use some functions provided by th e 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 functio n 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 usef ul. We have to avoid classic VBScript strings, which are unicode strin gs not suitable for parsing single bytes.
These are the only VBScript functions available to parse bytes. We sti ll 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 orde r to use that string as an argument in InstrB.
I wrote two functions for that purpose, they are called getString() an d 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 obje ct is part of VBScript and ASP2.0.
A first Dictionary object " UploadRequest " is defined. That dictionar y object contains all the controls submitted by the upload form. Keys are the names of the controls and Items are themselves dictionary obje cts 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 after wards.
Parser
Here is the code to parse, read and record upload controls. The proces s is done by a routine "BuildUploadRequest"; this routine takes only o ne argument that is the raw binary data RequestBin.
Sub BuildUploadRequest(RequestBin)
First we try to find the boundary. This boundary is useful to know whe n 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 w as written for that purpose: getByteString(String) method can convert the unicode VBScript string to a byte string. This function is describ ed at the end of the code explanations.
Let抯 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 Upl oadControl 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 " Cont ent-Disposition " header. The end of the name is delimited by the " ch aracter 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 t ype 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 nam e is a string of bytes, it is necessary to convert it to unicode so th at it can be used as a variant string variable. This is done by the ge tString() 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 th an the content. The content is converted to a unicode string so that i t 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 " Val ue ", 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 d ictionary 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,bound ary)
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
Here is the function that converts a string to a byte string. This fun ction is used to format arguments of the InstrB function.
'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
Uses of Upload Script
Here are sample uses of the upload script developed here. Samples file s and code are provided with the download file of this article. Extrac t the zip file to a directory and configure a virtual directory for yo ur web server. You can test and launch uploadForm.html from your brows er.
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 ret reive with Item() method. These item data can be stored in VBScript va riables 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抯 consider a scenario where you would like to access t he 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 cont ent-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 met hod.
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 b inary data in a file or in a database field rather than sending them b ack to the client. This "persistent" feature is one of the main goals of file uploading. The FileSystem object can be used to store the uplo aded 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 director y 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 dire ctory 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 f ile that contains the binary content of our original byte string. I ha ve 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.Server Variables_
("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 a lso be stored in the database to be able to display the data afterward s. 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 notic ed that when my binary data had an odd number of bytes, the appendChun k 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 solutio n 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 cod e is explained in detail. This starts with a brief recall of VBScript functions needed to manipulate strings and byte arrays. I then present ed the code of the upload script and the structure of the uploaded dat a.
Finally we showed several uses for this script, from simply using an u ploaded variable in ASP code to the storage of an uploaded file in a d atabase or in the file system.
-- ※ 来源:.月光软件站 http://www.moon-soft.com.[FROM: 202.103.97.83]
|
|