目的:在客戶端(瀏覽器)上像操作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是最好的了,其次是許可權的問題,特別是專案中涉及到一部分人能操作,一部分人只能檢視、下載的問題,這個等下一篇再寫了。。。(拖延一下……^-^)