來源: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)。