目的:在客户端(浏览器)上像操作window系统中的文件/文件夹一样,操作服务器上的某些指定文件/文件夹
效果图:
框架:jsp + springMVC + Tomcat
前台使用 elfinder
这是一个很好用的开源web文件管理器插件,用jquery+jquery-ui写的,在网上一搜文档好像也挺多的,于是准备搬到项目中来(挖坑开始),了解过后发现作者附带的后台demo是php写的,大多文档资料也是php的,java的特别少,出了问题也不知道是为什么,急死个人,前后折腾了两天才勉强能用了,在这里记录一下,以供大家参考,本人菜鸟,如果有什么理解不对的地方,欢迎各位指正。
打开官网,把代码dow下来:
打开压缩包:把选中的这些文件拷到项目中:
选中的可能有用,没选中的肯定没用(为什么?因为这没拷进项目里他也能跑,而且没问题...)
后台使用的是一个大神开源的基于java实现demo elfinder-2.x-servlet
这个目前还在持续更新中
接下来开始配置吧,基础版:
Maven项目中添加依赖项
1 <!-- web文件夹管理器jar包 --> 2 <dependency> 3 <groupId>com.github.bluejoe2008</groupId> 4 <artifactId>elfinder-servlet-2</artifactId> 5 <version>1.2</version> 6 <classifier>classes</classifier> 7 </dependency>
或者直接点击下载 elfinder-servlet-2.jar 包放到lib目录下
接下来在servlet.xml中配置需要spring管理的各对象
1 <!-- find appropriate command executor for given command--> 2 <bean id="commandExecutorFactory" 3 class="cn.bluejoe.elfinder.controller.executor.DefaultCommandExecutorFactory"> 4 <property name="classNamePattern" 5 value="cn.bluejoe.elfinder.controller.executors.%sCommandExecutor" /> 6 <property name="map"> 7 <map> 8 <!-- 9 <entry key="tree"> 10 <bean class="cn.bluejoe.elfinder.controller.executors.TreeCommandExecutor" /> 11 </entry> 12 --> 13 </map> 14 </property> 15 </bean> 16 17 <!-- FsService is often retrieved from HttpRequest --> 18 <!-- while a static FsService is defined here --> 19 <bean id="fsServiceFactory" class="cn.bluejoe.elfinder.impl.StaticFsServiceFactory"> 20 <property name="fsService"> 21 <bean class="cn.bluejoe.elfinder.impl.DefaultFsService"> 22 <property name="serviceConfig"> 23 <bean class="cn.bluejoe.elfinder.impl.DefaultFsServiceConfig"> 24 <property name="tmbWidth" value="80" /> 25 </bean> 26 </property> 27 <property name="volumeMap"> 28 <!-- two volumes are mounted here --> 29 <map> 30 <entry key="A"> 31 <bean class="cn.bluejoe.elfinder.localfs.LocalFsVolume"> 32 <property name="name" value="MyFiles" /> 33 <property name="rootDir" value="/tmp/a" /> 34 </bean> 35 </entry> 36 <entry key="B"> 37 <bean class="cn.bluejoe.elfinder.localfs.LocalFsVolume"> 38 <property name="name" value="Shared" /> 39 <property name="rootDir" value="/tmp/b" /> 40 </bean> 41 </entry> 42 </map> 43 </property> 44 <property name="securityChecker"> 45 <bean class="cn.bluejoe.elfinder.impl.FsSecurityCheckerChain"> 46 <property name="filterMappings"> 47 <list> 48 <bean class="cn.bluejoe.elfinder.impl.FsSecurityCheckFilterMapping"> 49 <property name="pattern" value="A_.*" /> 50 <property name="checker"> 51 <bean class="cn.bluejoe.elfinder.impl.FsSecurityCheckForAll"> 52 <property name="readable" value="true" /> 53 <property name="writable" value="true" /> 54 </bean> 55 </property> 56 </bean> 57 <bean class="cn.bluejoe.elfinder.impl.FsSecurityCheckFilterMapping"> 58 <property name="pattern" value="B_.*" /> 59 <property name="checker"> 60 <bean class="cn.bluejoe.elfinder.impl.FsSecurityCheckForAll"> 61 <property name="readable" value="true" /> 62 <property name="writable" value="false" /> 63 </bean> 64 </property> 65 </bean> 66 </list> 67 </property> 68 </bean> 69 </property> 70 </bean> 71 </property> 72 </bean>
这里配置就是服务器上的文件夹名称,服务器上是在你有tomcat所在盘的根目录下建一个叫tmp的文件夹,但在客户端(浏览器)上显示的就是你配置的名称:MyFiles
加载jar包后,为了查看后台接收数据的url,需要加载源文件(我给的jar包压缩包里)
我们打开这个类cn.bluejoe.elfinder.controller.ConnectorController可以看到映射路径为”connector”
这就是前台请求后台时的url路径(先暂时记住)
接着开始写前台页面(我用的是jsp页面):
可以直接拿elfinder那个包里的elfinder.html改,但他里面没有引入js和css,所以还是自己来写吧
最好按照下面给出的顺序导入,因为在最开始我没有注意,导致很多样式是乱的,响应到了错误的地方
导入jquery.js,版本稍高的好,因为我发现他的里面用的是jquery-3.*的版本,这个根据自己的路径来导
<script src="${pageContext.request.contextPath}/js/jquery-3.2.1.min.js" type="text/javascript" charset="utf-8"></script>
导入jquery-ui.js jquery-ui.css ,接下来的这些文件的路径都是根据最开始拷到项目中的elfinder包里去找
<link href="${pageContext.request.contextPath}/elfinder/jquery/jquery-ui-1.12.0.css" rel="stylesheet" type="text/css" media="screen" charset="utf-8">
<script src="${pageContext.request.contextPath}/elfinder/jquery/jquery-ui-1.12.0.js" type="text/javascript" charset="utf-8"></script>
导入elfinder.css、theme.css
<link rel="stylesheet" href="${pageContext.request.contextPath}/elfinder/css/elfinder.min.css" type="text/css" media="screen" charset="utf-8"> <link rel="stylesheet" href="${pageContext.request.contextPath}/elfinder/css/theme.css" type="text/css" media="screen" charset="utf-8">
导入elfinder.js
<script src="${pageContext.request.contextPath}/elfinder/js/elfinder.min.js" type="text/javascript" charset="utf-8"></script>
导入中文语言包elfinder.zh_CN.js,elfinder是支持国际化的,从2.0版本开始可以完美支持中文了,如果这里不导入,不配置,默认是英文的
<script src="${pageContext.request.contextPath}/elfinder/js/i18n/elfinder.ru.js" type="text/javascript" charset="utf-8"></script> <script src="${pageContext.request.contextPath}/elfinder/js/i18n/elfinder.zh_CN.js" type="text/javascript" charset="utf-8"></script>
在html标签中声明容器:
<div id="elfinder" ></div>
Js代码:
<script type="text/javascript" charset="utf-8"> $(document).ready(function() { $('#elfinder').elfinder({ url : '${pageContext.request.contextPath}/connector', //这里的请求地址对应controller中的地址 lang : 'zh_CN', //配置默认语言为中文 height : parseInt(window.screen.availHeight * 0.7) //配置高度为浏览器高度的0.7 }); }); </script>
此时启动项目应该能看到以下页面了
此时一个坑出现了,我传什么文件都提示“未知的命令:null”,google了几个小时才发现是因数servlet.xml中配置了
<bean id="multipartResolver" class="com.sctbyc.sware.controller.resourceLibrary.filter.CommonsMultipartResolver"> <property name="defaultEncoding" value="UTF-8" /> <property name="maxUploadSize" value="104857600" /> <property name="maxInMemorySize" value="2048" /> </bean>
两个冲突了,去掉CommonsMultipartResolver就可以,但是项目中其他地方用到的上传就无法使用了,度娘了很久说冲突可以写一个代理来解决(不太会),直接把别人的代码拿来(好几百行),发现并没卵用,气死个人,于是又开始疯狂搜索...
最后发现根本不用什么代理,CommonsMultipartResolver这个类中有一个public boolean isMultipart(HttpServletRequest request)方法,我们继承这个类,重写这个isMultipart方法返回true和false就可以达到是否使用这个类来处理上传了
此处使用拦截器来判断其是我们的elfinder的上传文件或是其他上传方式,这里主要是用请求url的方式来判断是否为elfinder的请求,分三个类,代码如下:
import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; public class MultipartContextFileter implements Filter { FilterConfig config; @Override public void destroy() { } @Override public void doFilter(ServletRequest srequest, ServletResponse sresponse, FilterChain chain) throws IOException, ServletException { boolean isData = false; HttpServletRequest req = (HttpServletRequest)srequest; // 根据web.xml中的配置,判断当前url是否跳过此过滤器 String excludeURL = config.getInitParameter("excludeURL"); if (excludeURL != null && !"".equals(excludeURL)) { if (req.getRequestURI().indexOf(excludeURL) != -1) { isData = true; } } if (isData) { String content_type = req.getContentType(); if (content_type != null && content_type.indexOf("multipart/form-data") != -1) { MyMultiPartRequest jakarta = new MyMultiPartRequest(req); jakarta.isData = true; req = jakarta; } } chain.doFilter(req, sresponse); } @Override public void init(FilterConfig arg0) throws ServletException { config = arg0; } }
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; /**
* 继承request,对其进行包装,以保存更多信息,用于保存判断是否是elfinder的请求,后面执行时可以判断是否跳过CommonsMultipartResolver的处理
*/ public class MyMultiPartRequest extends HttpServletRequestWrapper { public boolean isData = false; //是否执行自定义的CommonsMultipartResolver public MyMultiPartRequest(HttpServletRequest request) { super(request); } }
public class CommonsMultipartResolver extends org.springframework.web.multipart.commons.CommonsMultipartResolver { /** * 这里是处理Multipart http的方法。如果这个返回值为true,那么Multipart http * body就会MyMultipartResolver 消耗掉.如果这里返回false * 那么就会交给后面的自己写的处理函数处理例如刚才elfinder请求 * */ @Override public boolean isMultipart(HttpServletRequest request) { if(request instanceof MyMultiPartRequest){ MyMultiPartRequest trequest = (MyMultiPartRequest)request; if(trequest.isData){ return false; } } return super.isMultipart(request); } }
然后在web.xml中配置拦截器,使其生效
<filter> <filter-name>MultiPartFilter</filter-name> <filter-class>com.sctbyc.sware.controller.resourceLibrary.filter.MultipartContextFileter</filter-class> <init-param> <param-name>excludeURL</param-name> <param-value>connector</param-value> </init-param> </filter> <filter-mapping> <filter-name>MultiPartFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
好了,这下就可以正常使用了上传文件了,好TM开心,赶紧各种建文件夹,上传文件
此时第二个坑出现了(文件超过2M传上去就是一个blob文件,且只有几十k到1M多不等),第一反应这应该是个不完整的二进制文件,但为什么呢?F12打开浏览器,看了下发现上传文件时它一直在不停的发请求,原来是这个前端框架使用的大文件分段上传的技术,就是把一个文件切成很多小块,一直发请求,一点点的上传,而后台似乎并没有这样实现,所以造成了这种情况,相当于多大的文件,最后都只保存了最后一次上传的那一块,知道了原因,开始查elfinder的文档,看看他怎么说:果然还真有这样一个配置项:
他说默认是10M,这寻思也没有啊,我的文件超过2M就不行了,于是我就配置了一个这个,再在后面加了两个0,约等于1G了,再试,还是不行啊,超过2M就截断了,又开始查文档,以为是自己配置的姿势没对,弄了很久,不行,没办法,只能看他的elfinder.full.js了,看看是不是这其中有什么鬼,果然我发现了一个东西
这里默认为2M-8K的大小,和我们配置的大小中取一个,但使用的是Math.min,取得是其中小的一个,难怪我们的大了他就不用了,所能我们把他改成Math.max就可以使用我们配置的大小了,妈妈再也不用担心我给的容量不够了,注意这里查看的是elfinder.full.js(即原版),但我们引入的时候是引入的elfinder.mini.js(压缩版),所以要去mini.js中修改才有用,(因为mini版没有格式,不好找,这里告诉大家一个小技巧,可以Ctrl+F打开搜索框,搜索2097152,也就是上图里的数字,一下就找到了)(这里测试的时候因为本地tomcat给的空间不够,所报了一个OutMemoryError,内存溢出,不过不用担心,生产环境给的是16个G,随便他传)
还有一个问题就是上传时有一个选择目录,但好像支持得不太好,传不上去,也不知道怎么改,所以我索性就在elfinder.js中把这个给屏蔽了,过程如下:
浏览器中检查这个按钮,发现他的html代码为:
<div class="ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only elfinder-tabstop elfinder-focus"><span class="ui-button-text">
所以去elfinder.mini.js中搜索出来,在他之前加上一个判断,如果是选择目录,就返回一个空:
if(i=='selectFolder')return '<span></span>';
这里等于selectFolder是因为在elfinder.zh_CN.js 中可以找到 "选择目录"对应的英文就是“selectFolder”
到这里,基本配置就结束了,从使用上来说几乎是没有问题了。
剩下的就该考虑到部分需要优化的内容了:还记得我们最开始的时候说过,后台的jar包中给定了请求的url了,但只有一个,这很容易冲突,特别是项目大了过后,更大概率会出现了,所以我们就需要自己来定义url是最好的了,其次是权限的问题,特别是项目中涉及到一部分人能操作,一部分人只能查看、下载的问题,这个等下一篇再写了。。。(拖延一下……^-^)