簡單的ASP無組件上傳原理實踐總結!

來源:Posted on 2008-02-25 08:37 藍箭GZ我的原創文章

首先是網上的一個經典上傳代碼:
一.submit.htm
<html><title>example</title>
<body>
<form name="form1" method="post" action="upload.asp" enctype="multipart/form-data">
<input type=file name="myfile">
<input type=submit name="submit" value="
提交">
</form>
</body>
</html>

二.upload.asp
<%@ Language=VBScript %>
<%
function binarytoasi(byval varstr)
   asi=""
   for i=1 to 3
       asi=asi&chr(ascb(midb(varstr,i,1)))
   next
   binarytoasi=asi
end function

formsize=request.totalbytes
formdata=request.binaryread(request.totalbytes ) '
獲得上傳資料
bcrlf=chrB(13) & chrB(10)
divider=leftB(formdata,clng(instrb(formdata,bcrlf))-1)

Position=instrb(formdata,bcrlf & bcrlf)+4        '
上傳文檔資料開始位元組位置
filesize=instrb(Position+1,formdata,divider)-Position-4 '
上傳文檔的總長度    注:這個位置有錯,應改成-2
exnamestart=instrb(1,formdata,chrb(46),1)+1       ' 取擴展名的開始位置
exnameend=instrb(exnamestart,formdata,chrb(34),1)  ' 取擴展名的結束位置
exname=midb(formdata,exnamestart,exnameend-exnamestart)     ' 取擴展名

set dr=CreateObject("Adodb.Stream")
    dr.Mode=3: dr.Type=1:dr.Open
set dr1=CreateObject("Adodb.Stream")
    dr1.Mode=3:dr1.Type=1:dr1.Open  

dr.Write formdata
dr.Position=Position-1
dr.CopyTo dr1,filesize
dr1.SaveToFile "d:/test/temp_"&"."&binarytoasi(exname),2    '
文檔保存
set dr=nothing:set dr1=nothing
%>

分析:第一.
上傳通過:<input type=file name="myfile">以及<form name="form1" method="post" action="upload.asp" enctype="multipart/form-data">中的enctype="multipart/form-data" 這種方式,就會在提交時產生如下FORM包括的控件及文件myfiler的內容:

注:<input ...>的TYPE的值有:
button  checkbox  file  hidden  image  password  radio  reset  submit  text


原TXT文件內容:
2008好運之年![堅師傅電腦維修中心]正式開張,電腦殺毒、遠程維護、家庭組網....統一價:38元(新張8折)。

通過在upload.asp中<%Response.BinaryWrite FormData %>輸入提交后接收到的所有內容

-----------------------------7d81191c10748 Content-Disposition: form-data; name="file1"; filename="D:\Documents and Settings\Administrator\My Documents\2008好運之年.txt" Content-Type: text/plain 2008好運之年![堅師傅電腦維修中心]正式開張,電腦殺毒、遠程維護、家庭組網....統一價:38元(新張8折)。 -----------------------------7d81191c10748 Content-Disposition: form-data; name="submit" 提交 -----------------------------7d81191c10748--

如何取得文件的開始位元組位置:
紅色字部分是一個分隔符,每次長度及名稱都不同,但同一次提交3個是相同的,而且通過WINHEX軟體查看到文章內容的開始處肯定有0D0A0D0A(藍色字中間的空格的內容的二進制值)如圖:




所以通過:

bcrlf=chrB(13) & chrB(10)    '0D0A的二進制值

Position=instrb(formdata,bcrlf & bcrlf)+4

注:InStr 函數

InStr([start, ]string1, string2[, compare])

從start位置開始找出string2在string1的第一個位置,compare有0(不寫就預設=0)=執行二進制比較,1=執行文本比較(我理解,0就是比較時區分大小寫,1就是不區分大小寫)

找到0D后要+4個位元組才是文章的開始位元組。

如何取得文件的長度:
試過通過文件結束處的0D0A,用Fileend=instrb(Position+1,formdata,bcrlf)-1,找到文件最后一個位元組
文件長度=Fileend-Position+1
有個問題就是小文件可以,大文件時,文件中可能有許多0D0A,這樣就出錯了!

正確方法是:找到文件內容后的分隔符位置,再-2(原程序中-4不知是什麼原因,會造成txt,jpg文件(少了兩個位元組)上傳后打開正常,而RAR,EXE,GIF就打開出錯)再-文件開始位置,即:

filesize=instrb(Position+1,formdata,divider)-2-Position

如何提取分隔符的字串值:

divider=leftB(formdata,clng(instrb(formdata,bcrlf))-1)

instrb(formdata,bcrlf) 定位在第一個0D0A的OD上,-1就定位在分隔符的最后一個位元組上。
注:leftB(str,num) 從左邊起取str的多少個位元組, clng(str)將字串轉成Long型數值。

為什麼要set dr=CreateObject("Adodb.Stream")set dr1=CreateObject("Adodb.Stream")
要產生兩個Adodb.Stream對象

由於SaveToFile方法,只能全部保存通過dr.Write formdata 寫入快取區的資料,而formdata不只是文件的資料,可通過copyto方法(可以從dr.position開始的,長度為filesize的資料)將所要用的文件資料利用dr作源,復制到目的dr1,將生成文件。

為什麼要dr.position=position-1 而不是dr.position=position 因為position就是文件的第一個位元組了!

經實踐發現,生成Adodb.Stream對象,文件指針位置為0,無論讀寫文件,都是先將文件指針position+1后再讀寫位元組資料,所以要生成文件前,要將文件指針先-1!

常見的兩個錯誤:
1.當上傳文件超過200KB時,報錯:“請求對象錯誤 'ASP 0104 : 80004005' 操作被禁止 /Upload.asp, line 40 ”
因IIS6限制了上傳大小為200KB
解決:先將IIS屬性中的“允許直接編輯配置資料庫”勾選


再找到 XX:\Windows\System32\Inetsrv 中的 metabase.XML,  修改 AspMaxRequestEntityAllowed=204800(200KB)。


2.當文件上傳限制已改為200MB后,上傳文件大於4MB時,報錯:“Response 對象 錯誤 'ASP 0251 : 80004005'
    
    超過響應快取區限制       /showuser.asp,行 0     此 ASP 頁的執行造成響應快取區超過其配置限制。”

解決辦法:找到 XX:\Windows\System32\Inetsrv 中的 metabase.XML,  修改 AspBufferingLimit="4194304"(4MB)。

    全站熱搜

    sleepingwolf 發表在 痞客邦 留言(0) 人氣()