java web项目 使用elfinder 实现文件管理器

时间:2024-01-24 09:03:30

  目的在客户端(浏览器)上像操作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路径(先暂时记住)

  

  看到这里的@controller,说明这就是后台的接收所有请求的入口,要让springmvc管理这个类,我们需要在springmvc-servlet.xml中加入这个类所在包的扫描

<context:component-scan base-package="cn.bluejoe.elfinder.controller" />

  

  接着开始写前台页面(我用的是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”,以及后台提示:unknown command:null,  google了几个小时才发现是因数servlet.xml中配置了

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.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>

  当然也不要忘记把自己刚刚写的处理文件上传的类配置到xml中,这样就spring就会自动创建及调用我们写的这个类了

  

<bean id="multipartResolver" class="我们定义的CommonsMultipartResolver这个类的全路径名">
     <property name="defaultEncoding" value="UTF-8" />
     <property name="maxUploadSize" value="104857600" />
     <property name="maxInMemorySize" value="2048" />
</bean>

 

  好了,这下就可以正常使用了上传文件了,好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是最好的了,其次是权限的问题,特别是项目中涉及到一部分人能操作,一部分人只能查看、下载的问题,这个等下一篇再写了。。。(拖延一下……^-^)