구글와이드(336x280)_상단 2개


(영문)Uploading Multiple Files ASP, ASP.NET

Uploading Multiple Files
By Sander Duivestein
Introduction
An ActiveX control I have written allows users to submit a file to a Web server. This component uses the WinSocket control to submit files using the file transfer protocol (FTP). It only worked in Internet Explorer 3.02 and higher, and several clients used Netscape, so I had to find another solution.
As I searched the Internet and several newsgroups I found out that I wasn뭪 the only one. An 11 March 1999, 15Seconds article by Doug Dean, 밆own and Dirty Browser Uploading with a VB ASP Component,?gave me a very good hint.
Dean뭩 component was simple and easy to use. Dean뭩 article said, however, that he had left us with the problem of 밶 multiple element header.?Consequently there was some work still to be done.
Before I started to work on my own component, I wanted to know what kind of functionalities other upload controls offered. So I looked at three other well-known components: the upload component of Software Artisans, the upload component of ASPUpload, and the Microsoft Posting Acceptor.
When I compared these components I found out that my component should meet the following requirements:
  1. The HTML form that submits the file should be a black box to the upload component. By this I mean that the component should accept all kinds of form fields and that the form field names and their values should be parsed by the component.
  2. It should be possible to give up a path to upload to.
  3. It should be possible to put a limit on the size of the files that were going to be submitted to the Web server.
  4. The component should be able to handle multiple files.
  5. The component should have a robust error handler.
  6. The component should perform well.
  7. The component should work in Netscape Navigator as well as in Microsoft Internet Explorer.
  8. It should be possible to save files into a database.
  9. Only certain groups of users should be allowed to upload a file to the Web server.
As I looked at my list I saw that I had quite a challenge.
Solving the Problem
The first thing I had to do is create an HTML form that contained two form types: a simple text box and a file box. This gives the following code:
Listing 1: Upload.htm

<HTML>
<HEAD><TITLE>Upload</TITLE></HEAD>
<BODY>
<FORM NAME="frmUpload" METHOD="Post" ENCTYPE="multipart/form-data" ACTION="Upload.asp"> <TABLE>
<TR><TD>Author</TD><TD><INPUT TYPE="text" NAME="txtAuthor"></TD></TR>
<TR><TD>File</TD><TD><INPUT TYPE="file" NAME="txtFileName"></TD></TR>
<TR><TD COLSPAN="2" ALIGN="right"><INPUT TYPE="Submit" VALUE="Upload"></TD></TR>
</TABLE>
</FORM>
</BODY>
</HTML>


Use the ENCTYPE="multipart/form-data" to enable the form to submit a file. We also need a file to receive the file. This is how it looks:
Listing 2: Upload.asp
 
<%@ Language=VBScript %>

<%
Option explicit
Response.Buffer = True
On Error Resume Next

If Request.ServerVariables("REQUEST_METHOD") = "POST" Then

        Dim objUpload
        Dim lngMaxFileBytes
        Dim strUploadPath
        Dim varResult

        lngMaxFileBytes = 10000
        strUploadPath = "c:\inetpub\wwwroot\upload\"
        
        Set objUpload = Server.CreateObject("pjUploadFile.clsUpload")

        If Err.Number <> 0 Then

                Response.Write "The component wasn뭪 registered"

        Else

                varResult = objUpload.DoUpload (lngMaxFileBytes, strUploadPath)
                Set objUpload = Nothing

                멬rite the result
Dim i
                For i = 0 to UBound(varResult,1)
                        Response.Write varResult(i,0) & " : " & varResult(i,1) & "<br>"
                Next

        End If

End If
%> 
 
As you can see I wanted to be able to set the following two variables:
  1. lngMaxFileBytes - the maximum amount of bytes a file may contain, and
  2. strUploadPath - the location on the Web server were the file should be written.
I also added a little error handler to check whether the upload component was properly registered on the Web server. This was the only error I wanted to check for in the ASP page. If any other error occurred, I wanted the upload component to handle it.
The last thing I did was to declare a variable varReturn. This variable should receive the return value from the upload component. This return value should contain all the form field names and their values. As you can see by the FOR NEXT loop, this return value must be an array.
This was the easy part. Now we have to create an ActiveX component, which should handle the submitted form. So open up Visual Basic 6 and choose an ActiveX project (see Figure 1).
Figure 1: Creating an ActiveX dll Project

The first thing I did then was to add a reference to the Active Server Pages Object library. For this I clicked on Project on the menu bar and then selected References. In the following drop-down list I selected the Active Server Pages Object library (see Figure 2).
Figure 2 : Project References

With this library we are able to use the Request object of Active Server Pages. Before you can really use it, you must paste in the following code:
 
Option Explicit

Private MyScriptingContext As ScriptingContext
Private MyRequest As Request
Private MyResponse As Request

Public Sub OnStartPage(PassedScriptingContext As ScriptingContext)
    Set MyScriptingContext = PassedScriptingContext
    Set MyRequest = MyScriptingContext.Request
    Set MyResponse = MySriptingContext.Response
End Sub 
 
Why do we need the Active Server Pages Object library? Well, with the help of the Request object we can read the http stream which was sent to us by the upload.htm. There is however a big "but" ?When we try to read the form field names and their according values with, for example, Request.Form("txtTitle"), we aren뭪 able to read the rest of the raw data that was sent to us.
However, we can use the Request.TotalBytes and the Request.BinaryRead to read the data that was sent to us.
The first time I used the code from Doug Dean resulted in the following:
 
    '~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Dim varByteCount
    Dim binArray() As Byte

    '~~~~~ BYTE COUNT OF RAW FORM DATA ~~~~~~~~~~~~
    varByteCount = MyRequest.TotalBytes
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    '~~~~~ PLACE RAW DATA INTO BYTE ARRAY ~~~~~~~~~
    ReDim binArray(varByteCount)
    binArray = MyRequest.BinaryRead(varByteCount) 
 
Doug placed the raw data into a binary array, which of course worked quite fine. But when I tried to parse/filter the form field names and their values, I encountered a problem. Let뭩 say, for example, that we want to submit an HTML form that contains five text boxes and one file box. The file we want to submit has a size of 100K (=102,400 bytes).
Now our upload control receives over 102,400 bytes. All these bytes will be put into a binary array. When we want to parse the form field names and their values, we have to loop through the entire array. We have to loop through this array six times, because we submitted six form fields. When you try this, it works quite fine, but the performance goes down really hard.
So we need to find another way to can parse the data in the http header. When browsing the MSDN library, I found the solution. We must use Unicode!!! With Unicode we can put the data into a Visual Basic variant and then we can parse the variant, which contains a string.
So now my code looks like this:
 
Option Explicit

Private MyScriptingContext As ScriptingContext
Private MyRequest As Request
Private MyResponse As Request

Public Sub OnStartPage(PassedScriptingContext As ScriptingContext)
    Set MyScriptingContext = PassedScriptingContext
    Set MyRequest = MyScriptingContext.Request
    Set MyResponse = MySriptingContext.Response
End Sub

Public Function DoUpload (ByVal lngMaxFileBytes As Long, _
   ByVal strUploadPath As String) As Variant


멫ariables
Dim varByteCount As Variant
Dim varHTTPHeader As Variant

'Count total bytes in HTTP-header
varByteCount = MyRequest.TotalBytes
        
멌onversion of bytes to Unicode
 varHTTPHeader = StrConv(MyRequest.BinaryRead(varByteCount), vbUnicode)

멬rite HTTPHeader
 MyResponse.Write varHTTPHeader

Function End

 
So let뭩 see what happens when we try this code. For this test I used a .txt file named "warp11.txt" that contained the following sentence: "Warp11 builds state-of-the-art applications at the speed of light." The form we submit looks like this (Figure 3).
Figure 3: The HTML Form Sent
This is output the upload control writes into our browser:
 
-----------------------------7cf28c330254 Content-Disposition: form-data; 
name="txtAuthor" Sander Duivestein -----------------------------7cf28c330254 Content-
Disposition: form-data; name="txtFileName"; filename="C:\Download\Warp11.txt" 
Content-Type: text/plain Warp11 builds state-of-the-art applications at the speed of light. 
-----------------------------7cf28c330254 
 
As you can see this is nothing but a string, and we can parse this string. When an HTML form is submitted by using the multipart/form-data MME type, the form data is divided into seperate parts. Each part of the form data is divided by a boundary:
"-----------------------------7cf28c330254". For each form we submit the boundary will have a different string. In the upload application we can use this boundary to parse our formfields and their values, so we put this boundary string in an variable varDelimeter. We can do this by adding the following code:
 
Dim varDelimeter As Variant varDelimeter = LeftB(varHTTPHeader, 76)
 
The first thing we want to know is how many form fields are sent to the upload control. So we have to create a counter:
 
Dim  intFormFieldCounter As Integer

'Count formfields
intFormFieldCounter = Len(varHTTPHeader) - Len(Replace(varHTTPHeader, "; name=", Mid("; name=", 2))) 
 
When we know the number of form fields that are sent to the upload control, we can begin to loop through the varHTTPHeader, and then we can parse the form field names and their form field values.
When we find a form field name and the value, we want store these values. To store these values, we simply create a two-dimensional array:
 
'Array for containing formfields and their values ReDim arrFormFields(intFormFieldCounter - 1, 1) As Variant
 
Later, we want to return these values to the upload.asp file. So that is why we want the array to be of the variant type. Variants are the only array types that ASP pages understand.
Now let뭩 parse the form fields and their names. First, we create a loop:
 
'Begin parsing formfield names and values For i = 0 To intFormFieldCounter - 1
 
Then we determine the position where the first form field element will start:
 
'Determine where FormFieldName starts lngFormFieldNameStart = InStrB(lngFormFieldNameStart + 1, varHTTPHeader, "; name=" & Chr(34))    
 
We now have to find out where the form field ends:
 
'Determine where FormFieldName ends
lngFormFieldNameEnd = InStrB(lngFormFieldNameStart +  _
Len(StrConv("; name=" & Chr(34), vbUnicode)), varHTTPHeader, Chr(34)) _
 + Len(StrConv(Chr(34), vbUnicode)) 
 
If we have the beginning and the ending position of the form field, we can filter the name of the form field:
 
'Filter FormFieldName
strFormFieldName = MidB(varHTTPHeader, lngFormFieldNameStart, lngFormFieldNameEnd - lngFormFieldNameStart)
        
'Remove "; name=" from string
strFormFieldName = Replace(strFormFieldName, "; name=", vbNullString)
        
멢emove quotes from string
strFormFieldName = Replace(strFormFieldName, Chr(34), vbNullString) 
 
If we have the name of the form field, we can look at what type of form field we are dealing with. We can do this by looking at if there is a ";" after the form field name. If this is true, then we are dealing with a file, otherwise we are dealing with a text field. So we have to create an IF THEN condition.
      
'Check for file
If MidB(varHTTPHeader, lngFormFieldNameEnd, 2) = ";" Then 
 
As we look at our header, we can see that the first time we are going into the loop this condition is False. This means that we are dealing with a normal text field. Now we can parse for the value that belongs to our form field name. First we have to find out where our form field value starts:
      
Else

멏etermine where formfieldvalue starts
lngFormFieldValueStart = lngFormFieldNameEnd 
 
Second, as before, we have to find out where our form field value ends:
      
'Determine where formfieldvalue ends lngFormFieldValueEnd = InStrB(lngFormFieldValueStart, varHTTPHeader, varDelimeter)
 
Now we can filter the form field value:
      
'Filter formfieldvalue
strFormFieldValue = MidB(varHTTPHeader, lngFormFieldValueStart, lngFormFieldValueEnd - lngFormFieldValueStart)
strFormFieldValue = Replace(strFormFieldValue, vbCrLf, vbNullString) 
 
At this moment we have parsed the form field name "txtAuthor" and the according form field value "Sander Duivestein." We can put these two values into the array, but before we do that we make the start postion of the next loop the same as the last position we ended parsing:
                  
lngFormFieldNameStart = lngFormFieldValueEnd
        
End If
        
'Assign formfieldnames and formfieldvalues to array
arrFormFields(i, 0) = strFormFieldName
arrFormFields(i, 1) = strFormFieldValue

Next 
 
The second time we are going into the loop, we are going for the file. The first thing we find is the form field name "txtFile." The second thing we find is that this time our condition is met:
 
'Check for file If MidB(varHTTPHeader, lngFormFieldNameEnd, 2) = ";" Then
 
We already know the form field name is "txtFile," but we don뭪 know the name of the file. So we determine the position of where the form field value starts:
 
lngFormFieldValueStart = InStrB(lngFormFieldNameEnd, varHTTPHeader, "filename=" & Chr(34)) 
 
Secondly we want to know the place where the form field value ends.
 
lngFormFieldValueEnd = InStrB(lngFormFieldValueStart +     Len(StrConv("filename=" & Chr(34), vbUnicode)), varHTTPHeader, Chr(34))
 
So now we can parse the name of the file.
                         
'Parse filename from HTTPHeader
strFileName = MidB(varHTTPHeader, lngFormFieldValueStart, lngFormFieldValueEnd - lngFormFieldValueStart)
strFileName = Mid(strFileName, InStr(strFileName, "=") + 2, Len(strFileName) - InStr(strFileName, "="))
            
'Replace quotes
 strFileName = Replace(strFileName, Chr(34), vbNullString) 
 
At this moment we have a string strFileName that contains the following value: "C:\temp\warp11.txt." We only want to know the name of the file, so we have to remove "C:\temp\." I have created a function called GetFileName to remove the path from a string. This function only returns the file name. The code for the function GetFileName looks like this:
 
Private Function GetFileName(strFilePath) As String
 
    Dim intPos As Integer
    
    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 
 
We call this function as follows:
 
'Remove path from filename strFileName = GetFileName(strFileName)
 
Now we know two things: the name of form field "txtfile" and the value of this form field "warp11.txt." At this moment I have built in two checks. The first check is to see if a file name was submitted:
 
'Check if a file was submitted
If Len(strFileName) = 0 Then
      Err.Raise ERR_NO_FILENAME
End If 
 
If the file name is empty, I raise an error and the application quits. I will come back to the error handling later in the article.
The second check I made was to see if the file has an extension:
 
'Check if file has an extension
If Not CheckFileExtension(strFileName) Then
       Err.Raise ERR_NO_EXTENSION
End If 
 
To check if an extension exists, I created a function called CheckFileExtension. This function returns TRUE if the file has an extension, otherwise it returns FALSE and I raise an error. The code for the function CheckFileExtension is as follows:
 
Private Function CheckFileExtension(strFileName) As Boolean

    Dim strFileExtension As String

    If InStr(strFileName, ".") Then
        strFileExtension = Mid(strFileName, InStrRev(strFileName, ".") + 1)
        If Len(strFileExtension) < 3 Then
            CheckFileExtension = False
        Else
            CheckFileExtension = True
        End If
    Else
        CheckFileExtension = False
    End If
    
End Function 
 
If the two checks don뭪 give any trouble, we can continue our search for the data of the file. We have to find out where the file data begins:
 
멏etermine where filedata begins lngFileDataStart = InStr(InStr(varHTTPHeader, strFileName), varHTTPHeader, vbCrLf & vbCrLf) + 4
 
And we have to find out where the file data ends:
 
'Determine where filedata ends lngFileDataEnd = InStr(lngFileDataStart, varHTTPHeader, varDelimeter)
 
If we have these two values, we can determine what the length of the file is:
             
Dim lngFileLength As Long lngFileLength = lngFileDataEnd - lngFileDataStart
 
At this point I built in two checks again. One check will see if there wasn뭪 an empty file submitted:
 
'Check to see if file exists
If lngFileLength <= 2 Then
      Err.Raise ERR_EMPTY_FILE
End If 
 
The other check is to verify that the total amount of bytes in the file does not exceed the maximum amount of bytes allowed.
 
멌heck for maximum bytes allowed
If Not lngMaxFileBytes = 0 Then
   If lngMaxFileBytes < lngFileLength Then
       Err.Raise ERR_FILESIZE_NOT_ALLOWED
   End If
End If 
 
If these two conditions are met, then we don뭪 have any trouble any more and we can write the file to the Web server. To write the file, we call the subprocedure WriteFile:
 
WriteFile strUploadPath, strFileName, Mid(varHTTPHeader, lngFileDataStart, lngFileLength) 
 
WriteFile uses the FileSystem object that comes with Microsoft Internet Information Server 4.0. Before we can use this FileSystem object we have to make a reference to this object. Therefore we add a reference to Microsoft Scripting Runtime. Again, in this function are two checks. One check finds out if the directory to upload to exists:
 
If Not fs.FolderExists(strUploadPath) Then
    Err.Raise ERR_FOLDER_DOES_NOT_EXIST
End If 
 
The other check determines if the file that was submitted to the Web server already exists. If so, then we raise an error.
 
If fs.FileExists(strUploadPath & strFileName) Then
    Err.Raise ERR_FILE_ALREADY_EXISTS
End If 
 
If these checks don뭪 give any trouble, we can write the file to the Web server:
 
'Create file
Set sFile = fs.CreateTextFile(strUploadPath & strFileName, True)
    
'Write file
sFile.Write varContent ', lngFileDataStart, lngFileLength
    
멌lose File
sFile.Close
Set sFile = Nothing
Set fs = Nothing 
 
We can exit the subprocedure, and we return to the loop we were in. Now we assign our file name to the array, and we leave the FOR NEXT loop. Everything went well, so we can return the array to our upload.asp page. The ASP page then shows our array in the browser.
In case of an error, the following happens. At the top of my function DoUpload I declared an error array:
 
Dim arrError(0, 1) As Variant arrError(0, 0) = "Error"
 
The errors that can occur are defined in the header of my Visual Basic project. The definition of the errors look like this:
 
'Error Definition
Private Const ERR_NO_FILENAME As Long = vbObjectError + 100
Private Const ERR_NO_EXTENSION As Long = vbObjectError + 101
Private Const ERR_EMPTY_FILE As Long = vbObjectError + 102
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 
 
When an error occurs, I assign an unique message to the arrError(0,1). So the ErrorHandler looks like this:
 
DoUpload_Err:

    Select Case Err.Number
        Case ERR_NO_FILENAME
            arrError(0, 1) = "There wasn't a file submitted to the server."
        Case ERR_NO_EXTENSION
            arrError(0, 1) = "The file contains no valid extension."
        Case ERR_EMPTY_FILE
            arrError(0, 1) = "The file you tried to upload doesn't exist or contains 0 bytes."
        Case ERR_FILESIZE_NOT_ALLOWED
            arrError(0, 1) = "Total bytes of file sent [" & lngFileLength &_  "] exceeds maximum bytes of file allowed [" &_  lngMaxFileBytes & "]."
        Case ERR_FOLDER_DOES_NOT_EXIST
            arrError(0, 1) = "The folder to upload to doesn't exist"
        Case ERR_FILE_ALREADY_EXISTS
            arrError(0, 1) = "The file [" & strFileName & "] already exists."
        Case Else
            arrError(0, 1) = Err.Description
    End Select 
 
We return this error array to the upload.asp. The error is then shown in the browser.
 
DoUpload = arrError()
    
Resume DoUpload_Exit 
 
Where are we now? We solved the first seven requirements of my list. So there are still two requirements left: uploading a file into a database and permitting only certain users to use the upload control. These two requirements will be solved in an upcoming article.
The only thing left to do is compile our project and register the pjUpload.dll on the Web server. We also have to create a Web folder. In this Web folder we place our two files - upload.htm and upload.asp. Now open up your browser and surf to http://[servername]/[webfoldername]/upload.htm.
Download the Code
You can download the complete source for the sample contained in this issue:

null



바보들의 영문법 카페(클릭!!)

오늘의 메모....

시사평론-정론직필 다음 카페
http://cafe.daum.net/sisa-1

바보들의 영문법 다음 카페
http://cafe.daum.net/babo-edu/

티스토리 내 블로그
http://earthly.tistory.com/

내 블로그에 있는 모든 글들과 자료에 대한 펌과 링크는 무제한 허용됩니다.
(단, 내 블로그에 덧글쓰기가 차단된 자들에게는 펌, 트랙백, 핑백 등이 일체 허용되지 않음.)

그리고 내 블로그 최근글 목록을 제목별로 보시려면....
바로 아래에 있는 이전글 목록의 최근달을 클릭하시면 됩니다.
그러면 제목을 보고 편하게 글을 골라 보실 수 있습니다.

그리고 내 블로그내 글을 검색하시려면 아래 검색버튼을 이용하시면 됩니다.


가가챗창

flag_Visitors

free counters