File Space——一个java structs2.0的小栗子(DIY)
1 概述
File Space系统适用于团体,主要提供的是团队文件分享以及个人文件的存储管理服务。分为个人文件存储管理平台和团队文件共享平台。
个人文件存储平台主要为个人提供相关的文件分类存储服务:该部分在用户登录后,可以选择添加新的文件,创建新的分类,修改原有的文件,查看相关的文件,以及对个人信息的管理等。
团队space主要是将个人部分添加的资料进行共享性的展示。
个人用户可以浏览团队共享的内容,然后在已登录的情况下,可以将浏览到的自己喜欢的资料添加到自己的收藏。
项目针对半封闭的团队管理方式,因此用户添加方式采用管理员添加方式。
详细的系统功能请看下图(图1-1)所示:
图:1-1
2 需求分析
2.1 系统需求概述
当今的时代推崇共享,工作团队中也需要共享。此系统为团队提供文件方面共享,通过为用户提供一个文件的存储管理平台,吸引用户进行文件上传,然后对上传的文件进行共享。
2.2 用例分析
该系统包括普通用户以及管理员两种活动者,此外,可以有游客,但是游客只能查看共享的概要信息。系统中,管理员用户是继承自普通用户,在普通用户的基础上多了对系统用户的管理功能。普通用户主要有系统的登陆登出,对于个人信息的管理,对于个人文件的管理,对于文件分类的管理以及对于收藏的管理,此外,下载文件功能单独列出,是基于用户既可以下载自己的文件,也可以下载共享区的文件。详细的用例情况请参考(图2-1)。
图:2-1
3 系统设计
3.1 技术路线
整体使用基于Model-View-Controller (MVC)模型的Struts2 web应用框架,使用JSP作为视图显示,实现前后端的分离,数据存储采用JDBC进行持久化存储。系统选用MYSQL数据库来存储数据,使用Tomcat7.0作为系统服务器。在页面表示方面综合应用了HTML5标准,JQuery等增加页面效果。此外,系统采用Tomcat自带的Tomcat JDBC Pool 增加数据访问的性能。
3.2 系统总体架构设计
总体基于MVC模式的B/S web系统,考虑扩展方便设计了4层架构:页面表示层(JSP)、请求处理转发层(Action)、业务逻辑层(Service)和持久化存储层(DAO)。
下图(图:3-1)是大部分B/S系统的架构图。
图:3-1
3.3 数据库设计
数据库根据系统的需要设计了用户表、文件表、文件类型表、收藏表,各个表中标示了实体的基本的必要属性,图中连线标示外键关系。用户与文件、文件类型、收藏都是1对多的关系,文件与收藏也是1对多的关系,同一个文件可以被不同的用户进行收藏,但每个用户只能单独收藏。此外,文件表与类型表是多对1的关系。图3-2中的连线标示外键关系。
图:3-2
3.4 系统详细设计
A.页面布局结构如下图3-3:
图:3-3
B.单页面内的操作采用调用Jquery方法,传递JSON数据的方式进行局部刷新。
C.进入首页需要执行action获取相关的文件数据,采用先进入一个页面,在该页面直接进行调用的方式实现。
D.密码使用MD5进行加密
E.使用拦截器在执行相关Action前进行登录状态的拦截。
F.使用数据库连接池提高系统性能。
G.使用存储过程,封装数据库操作。
H.使用Log4j记录DAO层的操作以及出错处理。
I.具体的Action方法实现逻辑参考功能实现。
4 系统实现
4.1 分页功能的实现
分页功能针对系统中显示的数据的数据非常多的时候采用的技术,目的是为了让系统按照页码展示相对应的数据记录,减少数据传递的数量,增加页面访问速度,提高用户体验。
分页实现采用定义分页类(图:4-1),然后需要使用分页的方法填写分页类的属性值,完成分页。
图:4-1 图:4-2
分页类定义了5个属性:当前页(currPage)、每一页显示的记录数目(ItemOfPage)、
总共的记录条数(totalItem)、总共的页数(totalPage)、对应页面的记录集合(items)。
其中当前页需要传递的前台数据,如果前台没有传递当前需要获取的是第几页则默认为第一页;每一页显示的记录数目采用的默认显示10条的方式,用户无法修改;总共的记录条数需要读取数据库中的数目,采用分页的需要首先在DAO层定义读取数据库所有数据数目的方法,然后在service层进行调用,然后填写到Pager对象中;总共的页数需要通过总共的记录条数以及每页显示的条数进行计算,然后将结果填入;最后根据该Pager对象的属性获取相应的数据库记录填写到Items列表中,传递到前台进行显示。
计算总共页数的方法:
totalItem%pager.getItemOfPage()==0?(totalItem/pager.getItemOfPage()):(totalItem/pager.getItemOfPage()+1)
4.2 日志功能的实现
日志采用Log4j的方式,首先导入如图(图4-2)的日志包,然后配置log4j.properties文件(图4-3)。然后在需要添加日志的页面通过log4j包中的方法获取日志对象,填写相应的日志信息。
private static Logger logger = Logger.getLogger(UserDaoImpl.class);
图:4-3
4.3 MD5加密(密码)的实现
首先定义加密类MD5以及加密的静态方法toMD5(),然后在需要加密的地方通过MD5调用静态方法进行加密。
toMD5()方法采用网上的博客教程,实现如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public final static String toMD5(String s) { 2 3 char hexDigits[]={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 4 5 try { 6 7 byte[] btInput = s.getBytes(); 8 9 // 获得MD5摘要算法的 MessageDigest 对象 10 11 MessageDigest mdInst = MessageDigest.getInstance("MD5"); 12 13 // 使用指定的字节更新摘要 14 15 mdInst.update(btInput); 16 17 // 获得密文 18 19 byte[] md = mdInst.digest(); 20 21 // 把密文转换成十六进制的字符串形式 22 23 int j = md.length; 24 25 char str[] = new char[j * 2]; 26 27 int k = 0; 28 29 for (int i = 0; i < j; i++) { 30 31 byte byte0 = md[i]; 32 33 str[k++] = hexDigits[byte0 >>> 4 & 0xf]; 34 35 str[k++] = hexDigits[byte0 & 0xf]; 36 37 } 38 39 return new String(str); 40 41 } catch (Exception e) { 42 43 e.printStackTrace(); 44 45 return null; 46 47 } 48 49 }
4.4 登录权限控制的实现
系统的权限控制是通过拦截器实现。
首先定义该拦截器,在执行相关的Action前进行拦截,该拦截器的实现通过继承
MethodFilterInterceptor并重写doIntercept方法实现。
关键代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 HttpServletRequest request = ServletActionContext.getRequest(); 2 3 User user = (User) request.getSession(false).getAttribute("user"); 4 5 if(user == null){ 6 7 //logger.info("用户没有登录!"); 8 9 return "re"; 10 11 } else { 12 13 //logger.info("用户登录成功!"); 14 15 return invoker.invoke(); 16 17 }
4.5 数据库连接池的实现
数据库连接池能够有效的提高访问数据库的速度,因此采用。
该项目使用Tomcat7.0 自带的Tomcat JDBC Pool,通过在项目中添加context.xml中配置数据源,然后通过获取context中的数据源获取数据库的连接。
4.6 文件上传功能的实现
文件上传前台使用类型为file的input,因为中文文件名在下载时出现问题,难以传输,因此在这里限制上传的文件名必须是英文名。
后台上传实现直接就通过
ServletActionContext.getServletContext().getRealPath("/upload");
获取文件上传的地址(项目下面的upload文件夹),然后将文件通过文件的输入输出流写入到服务器。
最后将文件的相关的存储信息写入到数据库,以便下载。
4.7 文件下载功能的实现
首先根据前台传递过来的文件的id值,获取该文件的相关信息,然后定义一个文件输出流以及相关的getter与setter方法,确定fileName以及contentType,最后配置输出的result
<result name="download" type="stream">
<param name="inputName">fileDown</param>
<param name="contentDisposition">attachment;filename="${fileFileName}"</param>
<param name="contentType">fileContentType;charset=utf-8</param>
</result>
4.8 查看文件列表功能的实现
文件列表分为区分用户与不区分用户的列表两种,分别对应首页的共享的文件展示与个人文件的展示,首页的文件展示因为文件数量巨大,因此采用的是分页的方式进行展示,个人文件里面的文件列表因为每一次展示都需要涉及文件的分类,因此数量不是很大,没有采用分页。
首页文件的共享展示,采用的分页方式过程请参考分页的实现
个人文件的展示通过session里面的用户对象以及前台传过来的文件类型的id值获取相关的文件对象列表进行实现。
4.9 数据库设计(用户区分,文件级联删除,存储过程)
数据库的具体设计见系统设计中的数据库设计。
用户区分采用的是在用户的属性中添加判断该用户是否是管理员的字段,根据该字段的值进行判断该用户是管理员还是普通用户。
文件删除功能,项目中没有提供单个文件的删除功能。
在用户删除某个文件类型的时候,根据外键关系,级联删除这些文件信息,在管理员删除该用户时,该用户下面的文件也会被删除。
项目的数据访问都采用的是存储过程。
采用Navicat直接在数据库定义存储过程,然后在DAO层中通过
prepareCall(“call XXX”);调用该存储过程获得数据。
4.10 面包屑导航功能的实现
面包屑导航的作用是告诉访问者他们目前在网站中的位置以及如何返回,当网站中出现典型的单线3层以上的层次使用面包屑导航可以有效的提高用户的体验。
本次项目中因为要实现文件分类的无限级别的分类,因此采用面包屑导航。
项目中的页面部分展示通过后台传递的Map集合直接读取显示。
Map里面存放的当前分类的所有的父类的id值(键)以及对应的类别名称(值)。
因为考虑到需要按照一定的顺序存放,Map采用的是LinkedHashMap。
Map里面键的集合里面的值通过数据库里面定义的函数实现。
该函数通过传递的当前的类别的id值获取该类别的所有的父类并且按照一定的顺序,形成字符串输出。
该函数如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 BEGIN 2 3 DECLARE sParentList varchar(1000); 4 5 DECLARE sParentTemp varchar(1000); 6 7 SET sParentTemp =cast(rootId as CHAR); 8 9 WHILE sParentTemp is not null DO 10 11 IF (sParentList is not null) THEN 12 13 SET sParentList = concat(sParentTemp,',',sParentList); 14 15 ELSE 16 17 SET sParentList = concat(sParentTemp); 18 19 END IF; 20 21 SELECT group_concat(parentid) INTO sParentTemp FROM tab_type where FIND_IN_SET(tid,sParentTemp)>0; 22 23 END WHILE; 24 25 RETURN sParentList; 26 27 END
源码地址:https://github.com/husky00/Java/tree/master/Java/FileSpace01
sql地址:https://files.cnblogs.com/files/husky/FileSpace.zip 用的mysql 数据库,sql 使用navicat自动导出的!