WEB文档提示下载的研究

来源:互联网 发布:淘宝点客服旺旺没反应 编辑:程序博客网 时间:2024/04/28 18:14
对于网站资料提供下载的功能,我们通常不经考虑的做法,使用HTML中的A标签,href属性直接指向相关文件
例如 <A href="test.pdf">test.pdf</A>
最后客户通过浏览该网页点击此链接后,在WIN7使用IE8或者FIREFOX的时候,如果本地没有安装Adobe的reader软件,浏览器此时会提供用户下载该文件,但本地安装reader后,且没有做相关设置,则会在浏览器中直接打开该文件,用户如需保存则需要在reader的菜单中选择文件->另存为来保存。

以上是pdf文件的情况,来换成doc的WORD文档试试,结果浏览器弹出下载框,提示下载或者打开,在IE8下和FIREFOX下都是如此

如果换成jpg图片,此时则两个浏览器的结果都是在浏览器窗口中显示该图像。

对于以上的情况,虽然用迅雷等下载工具可以直接下载,但对于苛求的用户或者网站管理者,依然有他自己的使用需求。
1. 希望不管任何文件,只要是网站可提供下载的,点击后都能在客户端提示下载保存
2. 用户如果没有登录的情况下,即使得到发布的下载连接,用户也无法通过此链接下载文件,页面也会转到登录界面

所以自己研究了下相关的下载提示技术,这里整理如下,对应WEB语言是ASP的,其他如PHP的服务端语言也类似

要点一 连接交给服务端处理,而不用实际的文件名做链接地址
连接地址(A标签中的href属性值)在上面的例子中我们直接写了test.pdf文件,如此则两点需求都无法满足,所以一般情况下我们用的方法是把该地址指向一个服务顿页面,例如test.asp页面,文件由该页面处理相关信息后反馈即可。

要点二 设置MIME协议的相关参数,对应的属性应该是文档头的Content-Disposition,翻译成中应该是 内容-配置 的意思。属性的设置值应该是 "attachment; filename=test.pdf" 其中attachment 的意思为附件,在网上查了下,没有找到其他的相关设置值, filename=xxxx 这个表示下载时最终保存的文件名称(包含后缀),在测试时试着刻意把文件名的书写省略,得到的结果返回的是A标签的href文件名称,例如href="test.asp" 保存提示的就是asp文件,当然这个虽然是asp文件,但其中的服务端代码都被解释过了,所以是不可见的。
对于 Content-Disposition 的设置书写,在网上的资料中基本都是用服务端语言的书写方式,例如ASP下是
response.AddHeader "Content-Disposition","attachment; filename=test.pdf"
自己考虑属于其实该设置属于head端的参数,是否也能加在相关html代码中,于是书写的测试代码如下

<!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" />
<meta http-equiv="Content-Disposition" content="attachment; filename=test.jpg" />
<title>下载测试</title>
</head>
<body>
<img src="test.jpg"/>
</body>
</html> 

因考虑没有直接加载pdf文件的html标签,于是这里临时改用图片test.jpg来代替,最终运行结果没有如预期的那样弹出下载框,而是直接跳转到该页面显示图像,而且是HTML页面的呈现方式。故应该加载在head中的Content-Disposition没有生效,或者说html页面中的MIME协议已经被HTML代码确定了,无法再次更改,总之这个问题我没有搞明白。

然后测试代码调整成
<% 
    Response.AddHeader "Content-Disposition", "attachment; filename=test.jpg"
    Response.ContentType = "image/jpg"
%>
<img src="test.jpg">
顺带一说,这里的contentType实际对应的就是上面没有运行成功的html代码head头中的第一个meta标签内容,但这里的MIME类型已经更改为image/jpg的jpg图像类型了,对应的文件都有相关的contentType字符,这个可以百度下contentType即可找到。
如此执行后弹出下载框倒是有了,但保存后虽然也是jpg文件,但无法正常打开,实际用记事本查看的时候,你会发现该jpg文件实际的内容就是<img src="test.jpg">,于是才知道问题在于图像的加载(或者说页面某些元素的加载)是异步请求的,即页面一次请求,返回HTML代码,HTML页面中包含的CSS样式表文件,还有图像文件等都是随后一次一次加载进来,当然主要问题不在这,而在于虽然页面的MIME类型改成对应的图片文件类型了,也有下载提示,但实际的文件内容还是HTML代码,所以保存后会展现不了图片,所以最后依然全部回归到ASP服务端代码解决。当然从以上测试来看Content-Disposition的设置的目的已经达到。

要点三 准备服务端代码
以上的测试方式都失败了,所以最终回归到服务端代码上进行处理。
具体代码如下
<%
    dim path
    path="test.jpg" 
    Set Ado = Server.CreateObject("Adodb.Stream")
    FilePath = Server.MapPath(path)            '文件实际物理路径
    Ado.Mode = 3                                          '1 读,2 写,3 读写。
    Ado.Type = 1                                           '1 二进制,2 文本。
    Ado.Open
    Ado.LoadFromFile FilePath                     '载入文件
    Response.AddHeader "Content-Disposition", "attachment; filename=test.jpg"
    Response.AddHeader "Content-Length", Ado.size             '通知浏览器接收的文件大小
    Response.ContentType="image/jpg"
    While i < Ado.Size                                                               '循环读取直到读完为止
        Response.BinaryWrite Ado.Read(1000)                         '输出二进制数据流
        Response.Flush                                                              '立即发送
        i = i + 1000 '累加计数器
    Wend
    Ado.Close '关闭文件对象
    set Ado=nothing
    Response.End 

 %>
代码说明,关于文件WEB路径转服务器物理路径就不说明了,ASP下的文件读取都需要物理路径。
问题在于两端红色文字 其1 不知道这里的模式设置为1服务端就报错是为何,网上也没有找到相关说明,因为我们程序功能仅涉及读取文件,所以很容易想到设置为1 但实际却出错。 其2 关于循环中的分段读取和Flush也不太好理解,网上仅提示跟缓存有关。这些待读者自行去搜索答案吧

到这里,基本需求1已经满足无误了,需要下载pdf的,只要把文件名相关的都改成test.pdf ,然后contentType改为application/pdf即可。
需求2其实只要扩展上面一段服务器代码及可,在最前加入用户权限判定 例如
userid=session("userid") 
sql="SELECT * FROM users WHERE userid"& userid
set Mrc=server.createObject("Adodb.recordset")
Mrc.open DBCONN,1,1       'DBCONN为数据库连接对象Adodb.connection,请自行准备
if Mrc.recordcount<=0 then
    response.write("您还没有登录,无法查看该文件")
    response.end()
end if
Mrc.close
如上就是用于检验用户是否登录的情况,当然用户权限,用户分组的检测用于从这些方面限制用户下载,也大同小异。
实际加载服务端后可做的限制和规范还有很多,比如文件名称的隐藏,可以把实际在服务器上保存的文件名用时间整形量的数字加后缀表示,真实的文件名则存放在数据库,用户下载的时候把真实文件名写在Content-Disposition的filename中,这样可以有效隐藏相关文件,避免用户通过规律的文件名遍历他没有权限访问的文件。也做到了即使公开文件连接也没有办法公开实际资源的情况。
原创粉丝点击