月光博客 » 编程开发 » ASP无组件上传的原理

ASP无组件上传的原理

  无组件上传一直是困扰大家的一个问题。其实原理非常简单,核心就是分析字符串。不过,实际操作时,却困难重重。其中的关键问题还是大家往往对原理的剖析不够深入,或是因为过程过于繁琐。

  客户端HTML使用post表单的方法上传文件,要浏览上传附件,我们通过<input type="file">元素,但是一定要注意必须设置form的enctype属性为"multipart/form-data"。

  后台asp程序中,以前获取表单提交的ASCII 数据,非常的容易。但是如果需要获取上传的文件,就必须使用Request对象的BinaryRead方法来读取。BinaryRead方法是对当前输入流进行指定字节数的二进制读取,有点需要注意的是,一旦使用BinaryRead 方法后,再也不能使用Request.Form 或 Request.QueryString 集合了。结合Request对象的TotalBytes属性,可以将所有表单提交的数据全部变成二进制,不过这些数据都是经过编码的。首先让我们来看看这些数据是如何编码的,有无什么规律可循,编段代码,在代码中我们将BinaryRead读取的二进制转化为文本,输出出来,在后台的upload.asp中(注意该示例不要上传大文件,否则可能会造成浏览器死掉)。

  要实时反映进度条,实质就是要实时知道当前服务器获取了多少数据?再回想一下我们实现上传的过程,我们是通过Request.BinaryRead(Request.TotalBytes)来实现的,在Request的过程中我们无法得知当前服务器获取了多少数据。所以只能通过变通的方法了,如果我们可以将获取的数据分成一块一块的,然后根据已经上传的块数我们就可以算出来当前上传了多大了!也就是说,如果我1K为1块,那么上传1MB的输入流就分成1024块来获取,例如我当前已经获取了100块,那么就表明当前上传了100K.当我提出分块的时候很多人觉得不可思议,因为他们都忽略BinaryRead方法不仅是可以读取指定大小,而且可以连续读取的。

  通过Request.BinaryRead获取提交数据,分离出上传文件后,根据数据类型的不同,保存方式也不同:对于二进制数据,可以直接通过ADODB.Stream对象的SaveToFile方法,将二进制流保存成为文件。对于文本数据,可以通过TextStream对象的Write方法,将文本数据保存到文件中。

  对于文本数据和二进制数据,是可以方便的相互转换的,对于上传小文件来说,两者基本上没什么差别。但是两种方式保存时还是有一些差别的,对于ADODB.Stream对象,必须将所有数据全部装载完才可以保存成文件,所以使用这种方式如果上传大文件将很占用内存,而对于TextStream对象,可以在文件创建好后,一次Write一部分,分多次Write,这样的好处是不会占用服务器内存空间,结合上面分析的分块获取数据原理,我们可以每获取一块上传数据就将之Write到文件中。我曾做过试验,同样本机上传一个200多MB的文件,使用第一种方式内存一直在涨,到最后直接提示计算机虚拟内存不足,最可恨是即使进度条表示文件已经上传完,但是最终文件还是没有保存上。而使用后一种方法,上传过程中内存基本上无什么变化。

  原理基本上是说清楚了,但是实际代码要比这复杂的多,要考虑很多问题,最麻烦在分析数据那部分,对于每一块获取的数据,要分析是不是属于描述信息,是表单项目还是上传的文件,文件是否已经上传结束……

  相信根据上面的描述,您也可以开发出您自己功能强大的无组件上传组件。

顶一下 ▲()   踩一下 ▼()

相关文章

  1. 1
    想起时正是忘记   说道:
    Class UploadedFile
    Public ContentType
    Public FileName
    Public FileData
    Public Property Get FileSize()
    FileSize = LenB(FileData)
    End Property
    Public Sub SaveToDisk(sPath)
    Dim oFS, oFile
    Dim nIndex
    If sPath = "" Or FileName = "" Then Exit Sub
    If Mid(sPath, Len(sPath)) <> "\" Then sPath = sPath & "\"
    Set oFS = Server.CreateObject("Scripting.FileSystemObject")
    If Not oFS.FolderExists(sPath) Then Exit Sub
    Set oFile = oFS.CreateTextFile(sPath & FileName, True)
    For nIndex = 1 to LenB(FileData)
    oFile.Write Chr(AscB(MidB(FileData,nIndex,1)))
    Next
    oFile.Close
    End Sub
    End Class
    %>
    支持(10反对(4回复
  1. 2
    1   说道:
    *_*

    T_T
    支持(8反对(3回复
  1. 3
    auto   说道:
    虽说错误严重,不过还是支持你改正一下吧,呵呵
    支持(8反对(4回复
  1. 4
    想起时正是忘记   说道:
    <%
    Class FileUploader
    Public File
    Private Sub Class_Initialize()
    Set File = Server.CreateObject("Scripting.FileSystemObject")
    End Sub
    Private Sub Class_Terminate()
    set File=nothing
    End Sub
    Public Property Get Form(sIndex)
    Form = ""
    End Property
    支持(11反对(8回复
  1. 5
    一国民   说道:
    偶不喜欢upload组件,容易出现漏洞,尤其asp勘称“全球垃圾代码”,呵呵,一点都不过,不过支持你!兄弟!
    支持(7反对(4回复
  1. 6
    路人甲   说道:
    上传最重要的因素是表单的METHOD为POST,且要求enctype标记为multipart/form-data,所以博主的代码,根本就连最基本的要素都没达到。

    再分析一下博主的代码,关键代码是以下两句:
    objStream.LoadFromFile strFileName
    objStream.SaveToFile Server.MapPath(GetFileName(strFileName)),2

    而这两句代码的第一句是从一个文件中载入内容,第二句则是写入到另一个文件中。
    再看第一个文件的文件名是从哪儿来的呢?恰恰是从参数传递过来的。而博主的代码中根本就没从FORM提交过来的数据中取文件内容,而是根据传递过来的文件路径去读一个文件,所以这个功能在主机为localhost(也就是本机调试)的时候通过,而在正常的远程网站上是无法实现的(因为根本上述两句代码中的第一句根本无法读到内容)。
    支持(6反对(5回复
  1. 7
    打抱不平   说道:
    难道不能先鼓励再指出问题吗?ashi, efei ,你愿意别人这样对待你的付出吗?
    支持(7反对(7回复
  1. 8
    machine   说道:
    你使用本机作服务器并且本机浏览吧?这样并没有实现上传,读的是服务器上的文件,只不过刚好你服务器和客户端都是同一台机,所以能读出来,我晕~

    这个帖子快点删掉吧,怪搞笑的……
    支持(7反对(7回复
  1. 9
    想起时正是忘记   说道:
    Private Function CByteString(sString)
    Dim nIndex
    For nIndex = 1 to Len(sString)
    CByteString = CByteString & ChrB(AscB(Mid(sString,nIndex,1)))
    Next
    End Function
    Private Function CWideString(bsString)
    Dim nIndex
    CWideString =""
    For nIndex = 1 to LenB(bsString)
    CWideString = CWideString & Chr(AscB(MidB(bsString,nIndex,1)))
    Next
    End Function
    End Class
    支持(5反对(5回复
  1. 10
    结石   说道:
    继续加油!!
    支持(4反对(4回复
  1. 11
    想起时正是忘记   说道:
    呵呵,代码很笨重,不过简单传个小文件还是可以的
    支持(4反对(5回复
  1. 12
    LUCK   说道:
    放到别的服务器就不能运行了吗?我是菜鸟,我要试试
    支持(2反对(3回复
  1. 13
    想起时正是忘记   说道:
    Else
    nPos = InstrB(nPos, biData, CByteString(Chr(13)))
    nPosBegin = nPos + 4
    nPosEnd = InstrB(nPosBegin, biData, vDataBounds) - 2
    End If
    nDataBoundPos = InstrB(nDataBoundPos + LenB(vDataBounds), biData, vDataBounds)
    Loop
    End Sub
    支持(7反对(9回复
  1. 14
    老萨   说道:
    只能本机调试通过,虚拟空间是不行的。
    支持(5反对(9回复
  1. 15
    想起时正是忘记   说道:
    If nPosFile <> 0 And nPosFile < nPosBound Then
    Dim oUploadFile, sFileName
    Set oUploadFile = New UploadedFile
    nPosBegin = nPosFile + 10
    nPosEnd = InstrB(nPosBegin, biData, CByteString(Chr(34)))
    sFileName = CWideString(MidB(biData, nPosBegin, nPosEnd-nPosBegin))
    oUploadFile.FileName = Right(sFileName, Len(sFileName)-InStrRev(sFileName, "\"))
    nPos = InstrB(nPosEnd, biData, CByteString("Content-Type:"))
    nPosBegin = nPos + 14
    nPosEnd = InstrB(nPosBegin, biData, CByteString(Chr(13)))
    oUploadFile.ContentType = CWideString(MidB(biData, nPosBegin, nPosEnd-nPosBegin))
    nPosBegin = nPosEnd+4
    nPosEnd = InstrB(nPosBegin, biData, vDataBounds) - 2
    oUploadFile.FileData = MidB(biData, nPosBegin, nPosEnd-nPosBegin)
    set File=oUploadFile
    支持(5反对(12回复

发表留言