刚开始自己写博客系统的时候,里面涉及到了文件上传与下载,基本上这是一个网站必不可少的功能了。但是,当时涉世未深,套路太浅,于是很傻很单纯的直接把文件路径放到页面中去,然后从数据库取出文件名,与路径一拼接,妥妥的,就能下载了,当时睡觉的时候还是很心满意足的,觉得下载也就这么回事。时至今日,回头想想,细思极恐。谁会傻到把网站文件目录放大大庭广众之下,任人宰割,现而今眼目下,哪个网站不是设置的权限下载,如果你没登录,那就抱歉了,滚粗。如果你等级不够,那抱歉了,滚粗,如果你积分不够,那抱歉了,滚粗........
当你费尽心思,注册完用户,充值点下载币之后,满心欢喜的去下载下来打开来赏心悦目,结果呢,驴头不对马嘴,挂羊头卖狗肉比比皆是。在增加权限的同时,受害的却是我们这些无辜的下载者.........废话扯远了,现在回到正题。我们来分析一下csdn这个全国最大的资源,资讯交流社区网站,虽然不提供毛片服务...不得不说,大佬就是大佬,手下人才济济,做的东西也是极其完善的。当你登录之后,你想下载某个东西,系统会首先判断你的积分是否足够,是否是vip,如果是vip或积分足够,就会允许你进行下载,如果不够,那,抱歉了,滚粗。去充值下载豆去吧。当你下载完之后,系统会自动记录你的用户Id和sourceId,当你下次下载的时候,系统会进行判断你是否下载过该资源,如果下载过,就无需扣除下载豆,直接行使下载功能了。
我觉得这是个尿点啊,假如我去超市买瓶营养快线,喝完之后,撸一发...不是,是直接拿着空瓶去,找售货员说,这是从你们店买的营养快线,我喝完了,再让我拿一瓶新的吧,理由很简单,我已经付过钱了,可以无限消费。他们不把我打出来算是够客气的了。但是,谁让人家是大佬呢,底气足,腰板硬。你们随便吃,我请客。
说说这个下载功能,大家可以看一下下载弹窗;
复制下来这个url链接如下:
http://dl.download.youkuaiyun.com/down11/20170318/90a33cf286f0dc8b4a93df98d8135d44.zip?
response-content-disposition=attachment
%3Bfilename%3D%22Navicat%20Premium%2011%2B%E6%B3%A8%E5%86%8C%E6%9C%BA.zip //下载的文件名
%22&OSSAccessKeyId=9q6nvzoJGowBj4q1 //数据库文件id
&Expires=1502677169 //过期时间,用来控制缓存的失效日期
&Signature=1Abcg49OA75i%2BUDuMB%2BpuyujIi0%3D //我给他叫做下载签名,行使下载功能时,md5随机生成的字符串,保存到session或者cookie中
这个signature是比较有意思的,我不知道csdn上是不是这样的作用,按我的理解是,扣除下载豆之后,会生成一个随机的md5加密数字签名,存入session或cookie中,或者更狠一点,存入数据表字段中,当你下载的时候,会优先判断signature,这个东西就是保证只有你本人可以下载,你把这个链接发给别人,别人也下载不了。
看一下它提供的url连接,先指定一个文件名,不过这个文件名是该文件的第二个名字,即存入数据库的路径名,数据库source表中有两个名字,一个字段是中文名,是给用户看的,即网站界面显示的名字,但是并不是服务器中该文件的真实文件名,真实文件名是一个uuid加密的名字,90a33cf286f0dc8b4a93df98d8135d44.zip// 2017.08.05 miki 文件二次命名,作为下载url链接,采用uuid加密命名,并截取2-18位字符,二次加密,增加安全性
String imageName = "file_"+RadomUtil.getUUID().substring(2, 18)+uploadFileName.substring(uploadFileName.lastIndexOf("."));
File newfile = new File(ServletActionContext.getServletContext().getRealPath("/image_upload/source_upload/file")+"\\"+imageName);
IOUtils.cp(upload, newfile); //因为要设置表单里没有的属性(setImage(),)所以重新创造了一个photo对象
source.setPath(imageName);
source.setUser(user);
这样做的好处有以下几点:1. 不会出现名字一样的情况
2. 增加安全性,防止别知道网站上传文件路径,通过文字字典猜出文件名,进而非法盗取文件。中国人嘛,命名习惯都差不多,不然怎么会出现这么多叫二狗,小明,小华,小王的。当然,也可以做一个拦截器,只要是出现非正常的网站路径url就拦截下来,但是如此的话,比这个就麻烦了,一个手就能解决的事情,干嘛要糟蹋一个黄花大闺女呢。
接下的response-content-disposition则是文件下载方式,在后端以二进制流的方式下载该资源到前台。
<!-- 下载文件流处理配置 2017.08.07 miki -->
<action name="Source_*" class="jiabin.action.shop.SourceAction" method="{1}">
<result name="de">source_detail.jsp</result>
<result name="kiss">source_download.jsp</result>
<result name="success" type="stream">
<param name="contentDisposition">attachment;filename=${source.path}</param>
<param name="inputName">result</param>
<param name="bufferSize">4096</param>
<param name="contentLength">${fileLength}</param>
</result>
<result name="error">3bug.jsp</result>
</action>
后端下载的代码如下:我是采用的struts2框架做的下载方式:
public String downKiss()throws Exception{
HttpSession session=request.getSession();
String sign=(String)session.getAttribute("signal");
if(sign!=null){
if(source_id>0){
source=sourceService.getSourceById(source_id);
//这样能够组织一个文件流
String filePath="\\image_upload\\source_upload\\file\\"+source.getPath();
result= ServletActionContext.getServletContext().getResourceAsStream(filePath);
// 2017.08.11 miki 用于浏览器弹出框显示文件大小,要使用绝对路径,不然会报错,找不到文件
fileLength=MikiUtil.GetFileSize("E:\\eclipse与项目文件\\Soft\\apache-tomcat-7.0.55\\webapps\\miki\\source_upload\\file\\"+source.getPath());
source.setDownNum(source.getDownNum()+1);
sourceService.save(source);
//下载完,移除下载令牌,防止二次下载 2017.08.10 miki
//session.removeAttribute("signal");
return SUCCESS;
}else{
return "error";
}
} else{
//不符合要求,返回一个跳转到error.jsp的地址给用户,不返回文件流
return "error";
}
}
现而今眼目下,大致功能思路也就这样啦,当然这只是我的臆想,我做下载权限的时候,是参照着csdn纸面效果,然后按着本人的猜想做的,并且效果还不错。
条条大路通罗马,大家可以用更好的方法去实现,最好不过了,社会在发展,人类在进步。