<p>页面表单容器类,用来封装对表单数据的二进制读取等操作,是无组件上传的核心程序。其中有用到一个文件类,文件类的代码见:<a href="http://www.myfootprints.cn/blog/post/329.html">http://www.myfootprints.cn/blog/post/329.html</a></p>
<!--#include file="CFile.asp"-->
<% 
'**************************************************** 
'文件名: CFormDataGetter.asp
'描  述:这是一个页面表单容器,可以分析每个表单元素的内容,也可用作无组件上传类。
'	
'
'**************************************************** 
'# *Using CFile.asp*

Class CFormDataGetter
    ' 表单字节大小
    Private lFormSize
    ' 表单数据
    Private bnsFormData
    ' 表单数据中字段间的分隔符
    private bnsDivider
    Private bnsVbCrLf
    Private lChunkBytes
    Private lReadedBytes
    
    ' 字段分隔符
    Public Property Get FieldDivider()
        FieldDivider = bnsDivider
    End Property
    
    Public Property Get FormSize() 
        FormSize = lFormSize
    End Property
    
    Public Property Get FormBinaryData()
        FormBinaryData = bnsFormData
    End Property
    
    Public Property Get Chunk()
        Chunk = lChunkBytes
    End Property
    
    Public Property Let Chunk(ByVal l)
        lChunkBytes = l
    End Property
    
    Public Property Get ReadedBytes()
        ReadedBytes = lReadedBytes
    End Property
    
    Public Property Let ReadedBytes(ByVal l)
        lReadedBytes = l
    End Property
    
    Public Property Get LogPath()
        On Error Resume Next
        LogPath = Session("ssnLogPath")
        If Err.number <> 0 Then
            LogPath = sMF_WebSiteRootFolder
        End If
        On Error Goto 0
    End Property
    
    Public Property Let LogPath(ByVal s)
        Session("ssnLogPath") = s
    End Property 
    
    Public Property Get LogFileName()
        On Error Resume Next
        LogFileName = Session("ssnLogFileName")
        If Err.number <> 0 Then
            LogFileName = "DataReadLog.xml"
        End If
        On Error Goto 0
    End Property
    
    Public Property Let LogFileName(ByRef s)
        Session("ssnLogFileName") = s
    End Property
        
    Private Sub Class_Initialize
        ' 分块数
        Dim lChunks, i, lBytesToRead, oStream
        bnsVbCrLf = ChrB(13) & ChrB(10)
        
        ' 获取表单的总字节数
        lFormSize = Request.TotalBytes
        
        Me.Chunk = 100 * 1024
        Me.ReadedBytes = 0
        
        If lFormSize > 0 And Me.Chunk > 0 Then
            If lFormSize Mod Me.Chunk = 0 Then
                lChunks = lFormSize \ Me.Chunk
            Else
                lChunks = lFormSize \ Me.Chunk + 1
            End If
            
            Set oStream = Server.CreateObject("ADODB.Stream")
            oStream.Type = 1
            oStream.Mode = 3
            oStream.Open 
            
            ' 分块读取数据
            For i = 1 To lChunks
                ' 如果剩余的数据多于分块,则读进一个分块,否则读进剩余数据
                If lFormSize - Me.ReadedBytes > Me.Chunk Then
                    lBytesToRead = Me.Chunk
                Else
                    lBytesToRead = lFormSize - Me.ReadedBytes
                End If
                
                oStream.Write Request.BinaryRead(lBytesToRead)                
                Me.ReadedBytes = Me.ReadedBytes + lBytesToRead
                
                ' 记录读进了多少数据
                On Error Resume Next
                'LogDataReaded i, Now(), Me.ReadedBytes, lFormSize
                'LogDataReadInSession Me.ReadedBytes / lFormSize
                On Error Goto 0
            Next
            oStream.Position = 0
            bnsFormData = oStream.Read
            
            Set oStream = Nothing
            
            ' 下面开始查找表单数据中字段间的分隔符
            Dim lIndex
            
            lIndex = CLng(InstrB(bnsFormData,bnsVbCrLf))
            If lIndex >= 1 Then
                ' 成功获取到字段间的分隔符
                bnsDivider = LeftB(bnsFormData, lIndex - 1)
            Else
                '
                bnsDivider = ""
            End If
        Else
            bnsFormData = ""
            bnsDivider = ""
        End If
    End Sub
    
    '
    ' 记录上传了多少?
    '
    Public Function LogDataReaded(ByVal lSerialNumber, ByVal sTimeStamp, ByVal lBytesReaded, ByVal lTotalBytes)
        Dim sFileFullVirtualName, sFileContent, oFS, oFile
        
        sFileFullVirtualName = Me.LogPath & Me.LogFileName
        sFileContent = "<?xml version=""1.0"" encoding=""utf-8""?>"
        sFileContent = sFileContent & "<datareaded>"
        sFileContent = sFileContent & "<serialnumber>" & lSerialNumber & "</serialnumber>"
        sFileContent = sFileContent & "<timestamp>" & sTimeStamp & "</timestamp>"
        sFileContent = sFileContent & "<bytesreaded>" & lBytesReaded & "</bytesreaded>"
        sFileContent = sFileContent & "<totalbytes>" & lTotalBytes & "</totalbytes>"
        sFileContent = sFileContent & "</datareaded>"
        
        Set oFS = Server.CreateObject("Scripting.FileSystemObject")
        Set oFile = oFS.CreateTextFile(Server.MapPath(sFileFullVirtualName), True)
        
        oFile.Write sFileContent
        oFile.Close
        Set oFile = Nothing
        Set oFS = Nothing
        
    End Function
    
    '
    ' 记录上传了多少?
    '
    Public Sub LogDataReadInTxtFile(ByVal dPercent)
        Dim sFileFullVirtualName, sFileContent, oFS, oFile
        
        sFileFullVirtualName = Me.LogPath & Me.LogFileName
        sFileContent = dPercent
        
        Set oFS = Server.CreateObject("Scripting.FileSystemObject")
        Set oFile = oFS.CreateTextFile(Server.MapPath(sFileFullVirtualName), True)
        
        oFile.Write sFileContent
        oFile.Close
        Set oFile = Nothing
        Set oFS = Nothing        
    End Sub
    
    ' 
    ' 将上传了多少的信息记录到 Session 变量中
    '
    Public Sub LogDataReadInSession(ByVal dPercent)
        Session("ssnProgress") = dPercent
    End Sub
        
    '
    ' 获取指字字段名的二进制串
    '
    Public Function GetFieldBinaryData(ByRef sFieldName)
        Dim bnsBorder, lIndex, lFieldStart, lFieldEnd, lFieldSize
        
        If Me.FormSize <= 0 Then
            GetFieldBinaryData = ""
            Exit Function
        End If
        
        ' 字段开始边界
        bnsBorder = bnsDivider & bnsVBCrLf & ConvertStringToBinary("Content-Disposition: form-data; name=""" & sFieldName & """") & bnsVbCrLf & bnsVbCrLf
        lIndex = InStrB(1, Me.FormBinaryData, bnsBorder)
        If lIndex > 0 Then
            ' 定位到字段内容的开始位置
            lFieldStart = lIndex + LenB(bnsBorder)
            ' 定位到字段内容的结束位置
            lFieldEnd = InStrB(lFieldStart, Me.FormBinaryData, bnsDivider) - 3
            ' 计算字段内容的字节长度
            lFieldSize = lFieldEnd - lFieldStart + 1
            GetFieldBinaryData = MidB(Me.FormBinaryData, lFieldStart, lFieldSize)
        Else
            GetFieldBinaryData = bnsBorder
        End If
    End Function
    
    '
    ' 获取指定字段名的文本串
    '
    Public Function GetFieldTextData(ByRef sFieldName)
        GetFieldTextData = ConvertBinaryToString(GetFieldBinaryData(sFieldName))
    End Function
    
    '
    ' 将一个文本字符串转换成二进制字符串
    '
    Public Function ConvertStringToBinary(ByRef s)
        Dim bns, i
        
        For i = Len(s) To 1 Step -1
            bns = ChrB(Asc(Mid(s, i, 1))) & bns
        Next
        
        ConvertStringToBinary = bns
    End Function
    
    '
    ' 将一个二进制字符串转换成文本字符串
    ' ------------------------------------
    ' 此方法在localhost上能正确使用,得到理想的效果。但是将网站上传到服务器上时,有时会失灵。
    ' 在别的地方看到另一种程序来将二进制字符转换成文本字符串,和我的差不多,但是对于Ascii码大于等于128的,进行跳过,然后使用AscW()对连接两个字符同时进行转换。如下 
    ' Public Function ConvertBinaryToString(ByVal bns)
    '   Dim i, s, sClow
    '   For i = 1 To LenB(bns)
    '       sClow = MidB(bns, i, 1)
    '       If AscB(sClow) < 128 Then
    '           s = s & Chr(AscB(sClow))
    '       Else
    '           i = i + 1
    '           If i <= LenB(bns) Then s = s & Chr(AscW(MidB(bns, i, 1) & sClow))
    '       End If
    '   Next
    '   ConvertBinaryToString = s
    ' End Function
    '
    Public Function ConvertBinaryToString(ByVal bns)
        Dim s, i
        
        s = ""
        For i = LenB(bns) To 1 Step -1
            s = Chr(AscB(MidB(bns, i, 1))) & s
        Next
        
        ConvertBinaryToString = s
    End Function
    
    '
    ' 获取文件
    '
    Public Function GetFile(ByRef sFieldName) 
        Dim bnsBorder, lIndex, lFieldStart, lFieldEnd, lFieldSize, oFile
        
        If Me.FormSize <= 0 Then
            Set GetFile = Nothing
            'AddInfo "表单大小为0字节"
            Exit Function
        Else
            'AddInfo "表单大小为 " & Me.FormSize & " 字节"
        End If
        
        ' 文件二进制流开始边界
        bnsBorder = bnsDivider & bnsVbCrLf & ConvertStringToBinary("Content-Disposition: form-data; name=""" & sFieldName & """; filename=""")
        
        lIndex = InStrB(1, Me.FormBinaryData, bnsBorder)
        
        If lIndex > 0 Then
            Set oFile = New CFile
            ' 以下获取文件完整路径名
            '   定位到第1个字符
            lFieldStart = lIndex + LenB(bnsBorder)
            '   定位到最后1个字符
            lFieldEnd = InStrB(lFieldStart, Me.FormBinaryData, bnsVbCrLf) - 2
            '   计算路径字段内容大小
            lFieldSize = lFieldEnd - lFieldStart + 1
            If lFieldSize > 0 Then
                ' 文件名
                oFile.FullName = Cbns2TextStream(MidB(Me.FormBinaryData, lFieldStart, lFieldSize), "utf-8")
                
                ' 以下获取文件的MIME类型
                Dim lPos
                lPos = InStrB(lFieldEnd, Me.FormBinaryData, ConvertStringToBinary("Content-Type: "))
                If lPos > 0 Then
                    lFieldStart = lPos + LenB(ConvertStringToBinary("Content-Type: "))
                    lFieldEnd = InStrB(lFieldStart, Me.FormBinaryData, bnsVbCrLf & bnsVbCrLf) - 1
                    lFieldSize = lFieldEnd - lFieldStart + 1
                    If lFieldSize > 0 Then
                        oFile.MIME = ConvertBinaryToString(MidB(Me.FormBinaryData, lFieldStart, lFieldSize))
                    Else 
                        oFile.MIME = ""
                    End If
                Else
                    oFile.MIME = ""
                End If
                
                ' 以下获取文件内容
                lPos = lFieldEnd
                lFieldStart = InStrB(lPos, Me.FormBinaryData, bnsVbCrLf & bnsVbCrLf) + 4
                lFieldEnd = InStrB(lFieldStart, Me.FormBinaryData, bnsDivider) - 3
                lFieldSize = lFieldEnd - lFieldStart + 1
                If lFieldSize <= 0 Or lFieldStart <= 0 Then
                    oFile.BinaryStream = ""
                Else
                    Dim stmFormBinaryData, stmFileBinaryData
                    Set stmFormBinaryData = Server.CreateObject("ADODB.Stream")
                    Set stmFileBinaryData = Server.CreateObject("ADODB.Stream")
                    stmFormBinaryData.Type = 1
                    stmFormBinaryData.Open
                    stmFormBinaryData.Write Me.FormBinaryData
                    
                    stmFileBinaryData.Type = 1
                    stmFileBinaryData.Open 
                    ' 在ADODB.Stream对象里,索引从0开始,而不是VB的其他地方,索引从1开始
                    'stmFormBinaryData.Position = lFieldStart - 1
                    stmFormBinaryData.Position = 0
                    'stmFormBinaryData.CopyTo stmFileBinaryData, lFieldSize
                    stmFormBinaryData.CopyTo stmFileBinaryData
                    ' 使用MidB()或者LeftB()返回的字符串会自动添加一些别的信息,导致结果二进制串与原来的不太一样
                    'oFile.BinaryStream = MidB(Me.FormBinaryData, lFieldStart, lFieldSize)
                    stmFileBinaryData.Position = lFieldStart - 1
                    oFile.BinaryStream = stmFileBinaryData.Read(lFieldSize)
                    stmFormBinaryData.Close
                    stmFileBinaryData.Close
                    Set stmFormBinaryData = Nothing
                    Set stmFileBinaryData = Nothing
                End If
                Set GetFile = oFile
            Else
                oFile.BinaryStream = ""
                Set GetFile = oFile
            End If
        Else
            ' 未找到文件二进制流开始边界
            'AddInfo "未找到文件二进制流开始边界"
            'AddInfo "表单数据:" & Cbns2TextStream(Me.FormBinaryData, "utf-8")
            Set GetFile = Nothing
            Exit Function
        End If
    End Function
    
    '
    ' 保存文件
    '
    Public Function SaveFile(ByRef sFieldName, ByRef sFullName, ByVal iWriteMode) 
        Dim bnsBorder, lIndex, lFieldStart, lFieldEnd, lFieldSize, oFile
        
        If Me.FormSize <= 0 Then
            Set GetFile = Nothing
            Exit Function
        End If
        
        ' 文件二进制流开始边界
        bnsBorder = bnsDivider & bnsVbCrLf & ConvertStringToBinary("Content-Disposition: form-data; name=""" & sFieldName & """; filename=""")
        
        lIndex = InStrB(1, Me.FormBinaryData, bnsBorder)
        
        If lIndex > 0 Then
            Set oFile = New CFile
            ' 以下获取文件完整路径名
            '   定位到第1个字符
            lFieldStart = lIndex + LenB(bnsBorder)
            '   定位到最后1个字符
            lFieldEnd = InStrB(lFieldStart, Me.FormBinaryData, bnsVbCrLf) - 2
            '   计算路径字段内容大小
            lFieldSize = lFieldEnd - lFieldStart + 1
            ' 文件名
            oFile.FullName = ConvertBinaryToString(MidB(Me.FormBinaryData, lFieldStart, lFieldSize))
            
            ' 以下获取文件的MIME类型
            Dim lPos
            lPos = lFieldEnd
            lFieldStart = lPos + 18
            lFieldEnd = InStrB(lFieldStart, Me.FormBinaryData, bnsVbCrLf & bnsVbCrLf) - 1
            lFieldSize = lFieldEnd - lFieldStart + 1
            oFile.MIME = ConvertBinaryToString(MidB(Me.FormBinaryData, lFieldStart, lFieldSize))
            
            ' 以下获取文件内容
            lPos = lFieldEnd
            lFieldStart = lPos + 5
            lFieldEnd = InStrB(lFieldStart, Me.FormBinaryData, bnsDivider) - 3
            lFieldSize = lFieldEnd - lFieldStart + 1
            oFile.BinaryStream = MidB(Me.FormBinaryData, lFieldStart, lFieldSize)
            
            ' 开始保存文件
            Dim stm, stmFile
            
            If Trim(sFullName) = "" Or Right(sFullName, 1) = "\" Then Exit Function
            Set stm = Server.CreateObject("ADODB.Stream")
            Set stmFile = Server.CreateObject("ADODB.Stream")
            stm.Type = 1
            stm.Mode = 3
            stm.Open
            'stm.Write MidB(Me.FormBinaryData, lFieldStart, lFieldSize)
            stm.Write Me.FormBinaryData
            stmFile.Type = 1
            stmFile.Open 
            stm.Position = lFieldStart - 1
            stm.CopyTo stmFile, lFieldSize
            stmFile.SaveToFile sFullName, iWriteMode
            stm.Close
            stmFile.Close
            Set stm = Nothing
            Set stmFile = Nothing
            
            Set SaveFile = oFile
        Else
            Set SaveFile = Nothing
            Exit Function
        End If
    End Function
    
    '
    ' 将指定的二进制串转换成特定编码的文本
    '
    Public Function Cbns2TextStream(ByRef bns, ByRef sCharset)
        Dim stm
        
        Set stm = Server.CreateObject("ADODB.Stream")
        stm.Type = 2
        stm.Open
        stm.WriteText bns
        stm.Position = 0
        If Len(sCharset) > 0 Then stm.Charset = sCharset
        Cbns2TextStream = stm.ReadText
        stm.Close
        Set stm = Nothing
    End Function 
End Class
%>