logo

哈达波斯网|专注网站

ASP

ASP文件上传

ASP  2019/1/8 13:05:19  管理员  

由于VBScript这门古老的编程语言并没有像ASP.NET,Servlet、Struts2,PHP等封装好文件上传的方法,绝对不可能一个request.form["file"]就能够拿到文件,因此处理文件上传是很艰难的,需要自己截获HTTP传递过来的二进制报文,进行报文中的二进制字符切割,才能拿到文件以及我们想要的信息,不过这也更接近底层,更能让我们清楚HTTP文件上传的原理。


网上一些文章的VBScript的文件上传写得天华龙凤,短短的3KB代码居然能写到16KB,让人完全看不懂在干什么。或者就是用a,b,c命名,完全不知道怎么修改。而且大多数流传下来的文章都没有考虑到如果此网站是UTF-8编码的问题,容易造成乱码,毕竟文件上传涉及二进制操作,不是一句简简单单的Response.Charset="UTF-8"然后加个<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>就能完成的事,还需要对二进制文件流的编码进行修改。甚至还有些是用AspUpload来做,你不可能保证任何一个服务器都有这个组件吧?这会严重影响迁移问题。


下面,将为大家剖析如何在ASP做文件上传,让大家也写出属于自己的文件上传类!


将完成,如下图的一个文件系统:


20160120160844091.gif


不仅能够完成文件上传,还能自定义服务器保存文件的文件名,比如这里以原文件名+时间戳的方式保存文件,获取用户上传的后缀(你可以据此进一步加工,限定用户上传的文件类型了)等。


具体制作过程如下:


1、首先是这个工程的目录结构:


001.jpg


在进行代码写作之前,请保证你网站,至少是upload_file文件夹的Web共享是打开的,在属性界面有如下图的属性:

002.jpg

同时,Internet来宾账户有读取、写入的权限。

003.jpg


不然,有可能出现二进制流无法写入文件的情况,然而VBScript这门古老的编程语言是不会报错的,每次处理二进制流出现问题,它就给你看一个完全空白页面,就只会沉默,不跟你沟通,很蛋疼的。


2、之后是upload.html这个上传表单,本来没什么好说的,主要是表单有两个小地方需要注意的,这个表单必须给file标好name,同时表示这个表单是扔二进制数据上去upload.asp,同时必须用post的方式,而不是简简单单的文件。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>文件上传</title>
</head>
 
<body>
    <form method="post" action="upload.asp" enctype="multipart/form-data"><!--一定要有enctype="multipart/form-data",表示这个表单是传递二进制数据的,同时不能再传一些text,password这些无关的文件域上去了。-->
        <input type="file" name="file"/><!--这里的file文件域,就是一定要有name属性,虽然在之后的二进制处理根本没有用到这个name,你随便给个名称都行,但就是要有-->
        <input type="submit" value="提交" />
    </form>
</body>
</html>


如果这个file域没有name属性的话,无论你上传什么东西,upload.asp接受到的二进制流就只能是如下的东西,换句话说,是无法接受文件的。


004.jpg

3、最后就是这个工程的精华,upload.asp。


首先,你完全可以在upload.asp写入如下的代码:

<%
filesize=Request.TotalBytes
filedata=Request.BinaryRead(Request.TotalBytes)
response.BinaryWrite(filedata)
%>


将接受到的二进制数据打印出来,比如你将C盘的一个内容如下的aa.txt文件在upload.html的表单提交给upload.asp的话:

005.jpg


上述的代码,在upload.asp打印出来的东西如下:


006.jpg


接下来,你的目标很明了,就是对这串二进制字符进行切割,这串二进制内容里面有我们想要的一切信息,包括文件内容、客户端上传的文件名,


里面的-----------------7e010b37206c4其实是条分割线来的,如果你的表单里面有多个带name的文件域,则你会发现系统会用-----------------7e010b37206c4将多个文件报文分割。


同时,这里有部分回车在IE6浏览器中显示不出来,你可以用查看源代码的方式,看得一清二楚。


upload.asp具体代码如下所示:

<%@LANGUAGE="VBSCRIPT" CODEPAGE="65001"%>
<%
'禁止缓存'
Response.CacheControl="no-cache"  
Response.Expires=-1
Response.Charset="UTF-8"    '配合第一行设定网页编码
 
if Request.TotalBytes then	'如果上传文件非空'
	set read_stream=createobject("adodb.stream")	'设置一个流'
	read_stream.Type=1	'这个流读二进制数据,如果Type=2则读文本数据'
	read_stream.Open	'打开流'
	read_stream.write Request.BinaryRead(Request.TotalBytes)	'将表单传过来的二进制数据写入流read_stream'
	'将流read_stream的所有数据读到binary_stream中,binary_stream相当于一个临时变量,接下来将对binary_stream进行切割,以免污染read_stream中的原数据'
	read_stream.Position=0
	binary_stream=read_stream.Read
 
	enter=chrB(13)&chrB(10)	'二进制流中的回车'
	first_enter=clng(instrb(binary_stream,enter))	'寻找第一个回车的位置'
	second_enter=instrb(first_enter+1,binary_stream,enter)	'寻找第二个回车的位置'
 
	set write_stream=createobject("adodb.stream")	'定义一个流write_stream'
	write_stream.type=1	'write_stream是处理二进制数据的'
	write_stream.open
	'将read_stream中文件信息部分写到write_stream'
	read_stream.Position=first_enter+1
	read_stream.copyto write_stream,second_enter-first_enter-3
	
	write_stream.Position=0
	write_stream.type=2	'再将write_stream转为文本流'
	write_stream.CharSet="UTF-8"
	file_info=write_stream.readtext	'写到file_info这个字符串'
	write_stream.Close	'暂且关闭write_stream这个流,接下来对file_info这个字符串进行切割'
 
	file_name=mid(file_info,instrRev(file_info,"\")+1)	'取得全文件名'
	file_pre_suffix=left(file_name,instrRev(file_name,".")-1)	'取得文件前缀'
	suffix=mid(file_name,instrRev(file_name,"."))	'取得文件后缀,带.的'
   
	server_file_name=file_pre_suffix&"_"&datediff("s","1970-01-01 00:00:00",now)&suffix	'在服务器保存的文件名就是“原文件前缀_时间戳.原文件后缀名”'
	
	delimiter=leftB(binary_stream,clng(instrb(binary_stream,enter))-1)	'取得-----------------7e010b37206c4这个文件分隔符,用于给字符串处理函数找到文件内容'
	third_enter=instrb(second_enter+1,binary_stream,enter)	'找第三个回车的位置'
	file_begin_position=clng(instrb(third_enter+1,binary_stream,enter))+1	'获取文件内容第一个字符之前的位置'
	file_end_position=clng(instrb(lenb(delimiter),binary_stream,delimiter))-3  '获取文件内容中最后一个字符的位置,就是第二个分隔符"delimiter"开始的前一个二进制字符'
 
	write_stream.type=1	'write_stream是处理二进制数据'
	write_stream.open
	read_stream.Position=file_begin_position	'将流read_stream的开始位置移到文件开始的位置'
	read_stream.copyto write_stream,file_end_position-file_begin_position	'把流read_stream的开始位置之后 长为 文件长度 的内容复制到write_stream,其中文件长度就是文件结束的位置file_end_position-文件开始的位置file_begin_position'
	write_stream.SaveToFile server.mappath("upload_file/"&server_file_name),2	'将write_stream转化为文件,保存在设定好的文件目录'
 
	'人走带门,关闭所有用到的流'	
	write_stream.Close
	Set write_stream=nothing
	read_stream.Close
	Set read_stream=nothing
 
	'打印文件信息到网页'
	response.write "你上传的文件是:"&file_name&"<br>"
	response.write "你上传文件的后缀为:"&suffix&"<br>"
	url="http://"&Request.ServerVariables("HTTP_HOST")&"/my_asp/upload/upload_file/"&server_file_name
	response.write "访问地址:<a href='"&url&"' target='_blank'>"&url&"</a><br>"
	response.write "<a href='upload.html'>返回</a><br>" 
   
end if
%>

上述代码做的事,如下图所示:

007.jpg


说白了就是一个字符串切割的过程,虽然这个字符串切割过程有点复杂。


利用instr函数来确定字符在字符串位置,InStr(string1,string2)返回的是string2在string1出现的位置。例如:

SearchString="XXpXXpXXPXXP"'被搜索的字符串。
SearchChar="P"'要查找字符串"P"。
MyPos=Instr(SearchString,SearchChar)'返回9。

而Clng则是强行讲其转化为long型。

至于LEFT函数用于从一个文本字符串的第一个字符开始返回指定个数的字符,例如:

AnyString = "Hello World" '定义字符串。
MyStr = Left(AnyString, 1) '返回 "H"。
MyStr = Left(AnyString, 7) '返回 "Hello W"。
MyStr = Left(AnyString, 10) '返回 "Hello Worl"。

而Mid函数用法如下:

Mid(String As Variant, Start As Long, [Length As Variant]) As Variant


String,必选。变体(字符串)表达式,要被截取的字符。如果该参数为Null,则函数返回Null。


Start,必选。数值表达式,从左起第几位开始截取。


Length,可选,从Start参数指定的位置开始,要向右截取的长度。如果省略,将指定为从Start参数位置开始向右到字符串结尾的所有字符数。


例子如下:

v=Mid("VisualBasic",0,12)'提示实时错误5
v=Mid("VisualBasic",1,6)'v的值为"Visual"
v=Mid("VisualBasic",1,20)'v的值为"VisualBasic"
v=Mid("VisualBasic",8)'v的值为"asic"
v=Mid("VisualBasic",15)'v的值为空字符串
v=Mid("中文VB",2,2)'v的值为"文V"

当然还有right函数,不过left与带参数与不参数的mid函数足矣。

其实这些函数都是从Excel里面来的。唯一需要注意的是,在VBScript需要严格区分二进制的流的截取和字符串的截取,截取二进制里面的东西函数后面都补一个b,例如找二进制中字符出现的位置用instrb而不是原版的instr,instr是用来处理文本、字符串的。


form表单传递过来的一个二进制流,而部分二进制流的文本信息部分被转化为字符串来保存,这需要区别对待的。

网站首页  | 最新公告  | 漏洞修补  | 网站模板  | 知识文档  | 与我联系
Copyright © 2015 jlasp.com All Rights Reserved.
哈达波斯网|专注网站 版权所有
地址:吉林省吉林市昌邑区 联系QQ:383612004 联系人:董先生
未经本站授权,禁止复制或建立镜像,内容仅用于学习参考!
ICP备案号:吉ICP备15000891号-1 | 

吉公网安备 22020202000301号