青青子衿, 悠悠我心, 但为君故, 沉吟至今
« 百度要往哪里走—潜规则还是透明化?遭遇猎头公司 »

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的文件,使用第一种方式内存一直在涨,到最后直接提示计算机虚拟内存不足,最可恨是即使进度条表示文件已经上传完,但是最终文件还是没有保存上。而使用后一种方法,上传过程中内存基本上无什么变化。

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

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



  除非注明,月光博客文章均为原创,转载请以链接形式标明本文地址

  本文地址:http://www.williamlong.info/archives/380.html
  • 文章排行:
  • 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
    %>
  • 2006/7/5 17:16:47   支持(10)反对(4) 回复
  • 3.auto
  • 虽说错误严重,不过还是支持你改正一下吧,呵呵
  • 2006/4/25 8:03:01   支持(8)反对(4) 回复
  • 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
  • 2006/7/5 17:12:23   支持(11)反对(8) 回复
  • 5.一国民
  • 偶不喜欢upload组件,容易出现漏洞,尤其asp勘称“全球垃圾代码”,呵呵,一点都不过,不过支持你!兄弟!
  • 2006/11/26 21:12:51   支持(7)反对(4) 回复
  • 6.路人甲
  • 上传最重要的因素是表单的METHOD为POST,且要求enctype标记为multipart/form-data,所以博主的代码,根本就连最基本的要素都没达到。

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

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

    这个帖子快点删掉吧,怪搞笑的……
  • 2006/3/19 14:11:40   支持(7)反对(7) 回复
  • 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
  • 2006/7/5 17:16:11   支持(5)反对(5) 回复
  • 11.想起时正是忘记
  • 呵呵,代码很笨重,不过简单传个小文件还是可以的
  • 2006/7/5 17:18:09   支持(4)反对(5) 回复
  • 12.LUCK
  • 放到别的服务器就不能运行了吗?我是菜鸟,我要试试
  • 2007/7/31 17:28:52   支持(2)反对(3) 回复
  • 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
  • 2006/7/5 17:15:14   支持(7)反对(9) 回复
  • 14.老萨
  • 只能本机调试通过,虚拟空间是不行的。
  • 2008/7/6 21:34:12   支持(5)反对(9) 回复
  • 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
  • 2006/7/5 17:14:38   支持(5)反对(12) 回复

发表评论:

 请勿发送垃圾信息、广告、推广信息或链接,这样的信息将会被直接删除。

订阅博客

  • 订阅我的博客:订阅我的博客
  • 关注新浪微博:关注新浪微博
  • 关注腾讯微博:关注腾讯微博
  • 关注认证空间:关注QQ空间
  • 通过电子邮件订阅
  • 通过QQ邮件订阅

站内搜索

热文排行


月度排行

本站采用创作共用版权协议, 要求署名、非商业用途和相同方式共享. 转载本站内容必须也遵循“署名-非商业用途-相同方式共享”的创作共用协议.
This site is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 License.