精华区 [关闭][返回]

当前位置:网易精华区>>讨论区精华>>编程开发>>● ASP>>文件上传>>File Uploading with ASP and VBScript

主题:File Uploading with ASP and VBScript
发信人: 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]

[关闭][返回]