前言

最近在做工作流的事情,正好有个需求,要添加一个附件上传的功能,曾找过不少上传插件,都不是特别满意。无意中发现一个很好用的开源web文件管理器插件 elfinder,功能比较完善,社区也很活跃,还方便二次开发。

环境搭建

软件 地址
SpringBoot https://spring.io/projects/spring-boot/
elFinder https://studio-42.github.io/elFinder/

项目截图

周末抽时间做了一个简单的案例,希望对大家有所帮助,下面是简单的项目截图。



项目功能

在线新建目录、文件、附件上传、下载、预览、在线打包,图片在线裁剪、编辑,实现列表试图、图标视图等等一些列功能。

项目配置

项目在第三方插件进行二次开发,基于 SpringBoot 注解配置实现。

application.properties 配置:

  1. # 执行类,内部调用,实现前端相关功能
  2. file-manager.command=com.itstyle.cloud.common.elfinder.command
  3. file-manager.thumbnail.width=80
  4. file-manager.volumes[0].Node=
  5. file-manager.volumes[0].source=fileSystem
  6. file-manager.volumes[0].alias=file
  7. # 文件存放目录,可以自定义
  8. file-manager.volumes[0].path=D:/cloudFile
  9. file-manager.volumes[0]._default=true
  10. file-manager.volumes[0].locale=
  11. file-manager.volumes[0].constraint.locked=false
  12. file-manager.volumes[0].constraint.readable=true
  13. file-manager.volumes[0].constraint.writable=true

ElfinderConfiguration 读取配置:

  1. @Component
  2. @ConfigurationProperties(prefix="file-manager") //接收application.properties中的file-manager下面的属性
  3. public class ElfinderConfiguration {
  4. private Thumbnail thumbnail;
  5. private String command;
  6. private List<Node> volumes;
  7. private Long maxUploadSize = -1L;
  8. //省略部分代码
  9. }

elfinderStorageFactory 初始化 基础Bean:

  1. @Configuration
  2. public class ElFinderConfig {
  3. @Autowired
  4. private ElfinderConfiguration elfinderConfiguration;
  5. @Bean(name = "commandFactory")
  6. public CommandFactory getCommandFactory() {
  7. CommandFactory commandFactory = new CommandFactory();
  8. commandFactory.setClassNamePattern(elfinderConfiguration.getCommand()+".%sCommand");
  9. return commandFactory;
  10. }
  11. @Bean(name = "elfinderStorageFactory")
  12. public ElfinderStorageFactory getElfinderStorageFactory() {
  13. DefaultElfinderStorageFactory elfinderStorageFactory = new DefaultElfinderStorageFactory();
  14. elfinderStorageFactory.setElfinderStorage(getElfinderStorage());
  15. return elfinderStorageFactory;
  16. }
  17. @Bean(name = "elfinderStorage")
  18. public ElfinderStorage getElfinderStorage() {
  19. DefaultElfinderStorage defaultElfinderStorage = new DefaultElfinderStorage();
  20. // creates thumbnail
  21. DefaultThumbnailWidth defaultThumbnailWidth = new DefaultThumbnailWidth();
  22. defaultThumbnailWidth.setThumbnailWidth(elfinderConfiguration.getThumbnail().getWidth().intValue());
  23. // creates volumes, volumeIds, volumeLocale and volumeSecurities
  24. Character defaultVolumeId = 'A';
  25. List<Node> elfinderConfigurationVolumes = elfinderConfiguration.getVolumes();
  26. List<Volume> elfinderVolumes = new ArrayList<>(elfinderConfigurationVolumes.size());
  27. Map<Volume, String> elfinderVolumeIds = new HashMap<>(elfinderConfigurationVolumes.size());
  28. Map<Volume, Locale> elfinderVolumeLocales = new HashMap<>(elfinderConfigurationVolumes.size());
  29. List<VolumeSecurity> elfinderVolumeSecurities = new ArrayList<>();
  30. // creates volumes
  31. for (Node elfinderConfigurationVolume : elfinderConfigurationVolumes) {
  32. final String alias = elfinderConfigurationVolume.getAlias();
  33. final String path = elfinderConfigurationVolume.getPath();
  34. final String source = elfinderConfigurationVolume.getSource();
  35. final String locale = elfinderConfigurationVolume.getLocale();
  36. final boolean isLocked = elfinderConfigurationVolume.getConstraint().isLocked();
  37. final boolean isReadable = elfinderConfigurationVolume.getConstraint().isReadable();
  38. final boolean isWritable = elfinderConfigurationVolume.getConstraint().isWritable();
  39. // creates new volume
  40. Volume volume = VolumeSources.of(source).newInstance(alias, path);
  41. elfinderVolumes.add(volume);
  42. elfinderVolumeIds.put(volume, Character.toString(defaultVolumeId));
  43. elfinderVolumeLocales.put(volume, LocaleUtils.toLocale(locale));
  44. // creates security constraint
  45. SecurityConstraint securityConstraint = new SecurityConstraint();
  46. securityConstraint.setLocked(isLocked);
  47. securityConstraint.setReadable(isReadable);
  48. securityConstraint.setWritable(isWritable);
  49. // creates volume pattern and volume security
  50. final String volumePattern = Character.toString(defaultVolumeId) + ElFinderConstants.ELFINDER_VOLUME_SERCURITY_REGEX;
  51. elfinderVolumeSecurities.add(new DefaultVolumeSecurity(volumePattern, securityConstraint));
  52. // prepare next volumeId character
  53. defaultVolumeId++;
  54. }
  55. defaultElfinderStorage.setThumbnailWidth(defaultThumbnailWidth);
  56. defaultElfinderStorage.setVolumes(elfinderVolumes);
  57. defaultElfinderStorage.setVolumeIds(elfinderVolumeIds);
  58. defaultElfinderStorage.setVolumeLocales(elfinderVolumeLocales);
  59. defaultElfinderStorage.setVolumeSecurities(elfinderVolumeSecurities);
  60. return defaultElfinderStorage;
  61. }
  62. }

CloudDiskController 控制层实现:

  1. @Controller
  2. @RequestMapping("elfinder/connector")
  3. public class CloudDiskController {
  4. private static final Logger logger = LoggerFactory.getLogger(CloudDiskController.class);
  5. public static final String OPEN_STREAM = "openStream";
  6. public static final String GET_PARAMETER = "getParameter";
  7. @Resource(name = "commandFactory")
  8. private ElfinderCommandFactory elfinderCommandFactory;
  9. @Resource(name = "elfinderStorageFactory")
  10. private ElfinderStorageFactory elfinderStorageFactory;
  11. @RequestMapping
  12. public void connector(HttpServletRequest request, final HttpServletResponse response) throws IOException {
  13. try {
  14. response.setCharacterEncoding("UTF-8");
  15. request = processMultipartContent(request);
  16. } catch (Exception e) {
  17. throw new IOException(e.getMessage());
  18. }
  19. String cmd = request.getParameter(ElFinderConstants.ELFINDER_PARAMETER_COMMAND);
  20. ElfinderCommand elfinderCommand = elfinderCommandFactory.get(cmd);
  21. try {
  22. final HttpServletRequest protectedRequest = request;
  23. elfinderCommand.execute(new ElfinderContext() {
  24. @Override
  25. public ElfinderStorageFactory getVolumeSourceFactory() {
  26. return elfinderStorageFactory;
  27. }
  28. @Override
  29. public HttpServletRequest getRequest() {
  30. return protectedRequest;
  31. }
  32. @Override
  33. public HttpServletResponse getResponse() {
  34. return response;
  35. }
  36. });
  37. } catch (Exception e) {
  38. logger.error("Unknown error", e);
  39. }
  40. }
  41. //省略部分代码
  42. }

最后,前端页面引入:

  1. <div id="elfinder"></div>
  2. <script type="text/javascript" charset="utf-8">
  3. window.onload = function() {
  4. elFinder.prototype.loadCss('/elfinder/jquery-ui-1.12.1.custom/jquery-ui.css');
  5. $('#elfinder').elfinder({
  6. url : '/elfinder/connector',
  7. lang: 'zh_CN',
  8. height : window.innerHeight-20,
  9. commandsOptions: {
  10. edit: {
  11. editors : [
  12. {
  13. info:{
  14. name:'编辑',
  15. urlAsContent: false
  16. },
  17. // ACE Editor
  18. // `mimes` is not set for support everything kind of text file
  19. load : function(textarea) {
  20. var self = this,
  21. dfrd = $.Deferred(),
  22. cdn = './elfinder/ace/',
  23. init = function() {
  24. if (typeof ace === 'undefined') {
  25. console.log(cdn);
  26. this.fm.loadScript([
  27. cdn+'/ace.js',
  28. cdn+'/ext-modelist.js',
  29. cdn+'/ext-settings_menu.js',
  30. cdn+'/ext-language_tools.js'
  31. ], start);
  32. } else {
  33. start();
  34. }
  35. },
  36. start = function() {
  37. var editor, editorBase, mode,
  38. ta = $(textarea),
  39. taBase = ta.parent(),
  40. dialog = taBase.parent(),
  41. id = textarea.id + '_ace',
  42. ext = self.file.name.replace(/^.+\.([^.]+)|(.+)$/, '$1$2').toLowerCase(),
  43. // MIME/mode map
  44. mimeMode = {
  45. 'text/x-php' : 'php',
  46. 'application/x-php' : 'php',
  47. 'text/html' : 'html',
  48. 'application/xhtml+xml' : 'html',
  49. 'text/javascript' : 'javascript',
  50. 'application/javascript' : 'javascript',
  51. 'text/css' : 'css',
  52. 'text/x-c' : 'c_cpp',
  53. 'text/x-csrc' : 'c_cpp',
  54. 'text/x-chdr' : 'c_cpp',
  55. 'text/x-c++' : 'c_cpp',
  56. 'text/x-c++src' : 'c_cpp',
  57. 'text/x-c++hdr' : 'c_cpp',
  58. 'text/x-shellscript' : 'sh',
  59. 'application/x-csh' : 'sh',
  60. 'text/x-python' : 'python',
  61. 'text/x-java' : 'java',
  62. 'text/x-java-source' : 'java',
  63. 'text/x-ruby' : 'ruby',
  64. 'text/x-perl' : 'perl',
  65. 'application/x-perl' : 'perl',
  66. 'text/x-sql' : 'sql',
  67. 'text/xml' : 'xml',
  68. 'application/docbook+xml' : 'xml',
  69. 'application/xml' : 'xml'
  70. };
  71. // set basePath of ace
  72. ace.config.set('basePath', cdn);
  73. // set base height
  74. taBase.height(taBase.height());
  75. // detect mode
  76. mode = ace.require('ace/ext/modelist').getModeForPath('/' + self.file.name).name;
  77. if (mode === 'text') {
  78. if (mimeMode[self.file.mime]) {
  79. mode = mimeMode[self.file.mime];
  80. }
  81. }
  82. // show MIME:mode in title bar
  83. taBase.prev().children('.elfinder-dialog-title').append(' (' + self.file.mime + ' : ' + mode.split(/[\/\\]/).pop() + ')');
  84. // TextArea button and Setting button
  85. $('<div class="ui-dialog-buttonset"/>').css('float', 'left')
  86. .append(
  87. $('<button>文本框</button>')
  88. .button()
  89. .on('click', function(){
  90. if (ta.data('ace')) {
  91. ta.removeData('ace');
  92. editorBase.hide();
  93. ta.val(editor.session.getValue()).show().focus();
  94. $(this).text('编辑器');
  95. } else {
  96. ta.data('ace', true);
  97. editorBase.show();
  98. editor.setValue(ta.hide().val(), -1);
  99. editor.focus();
  100. $(this).text('文本框');
  101. }
  102. })
  103. )
  104. .append(
  105. $('<button>Ace editor setting</button>')
  106. .button({
  107. icons: {
  108. primary: 'ui-icon-gear',
  109. secondary: 'ui-icon-triangle-1-e'
  110. },
  111. text: false
  112. })
  113. .on('click', function(){
  114. editor.showSettingsMenu();
  115. })
  116. )
  117. .prependTo(taBase.next());
  118. // Base node of Ace editor
  119. editorBase = $('<div id="'+id+'" style="width:100%; height:100%;"/>').text(ta.val()).insertBefore(ta.hide());
  120. // Ace editor configure
  121. ta.data('ace', true);
  122. editor = ace.edit(id);
  123. ace.require('ace/ext/language_tools');
  124. ace.require('ace/ext/settings_menu').init(editor);
  125. editor.$blockScrolling = Infinity;
  126. editor.setOptions({
  127. theme: 'ace/theme/dawn',
  128. mode: 'ace/mode/' + mode,
  129. fontSize: '14px',
  130. wrap: true,
  131. enableBasicAutocompletion: true,
  132. enableSnippets: true,
  133. enableLiveAutocompletion: true
  134. });
  135. editor.commands.addCommand({
  136. name : "saveFile",
  137. bindKey: {
  138. win : 'Ctrl-s',
  139. mac : 'Command-s'
  140. },
  141. exec: function(editor) {
  142. self.doSave();
  143. }
  144. });
  145. editor.commands.addCommand({
  146. name : "closeEditor",
  147. bindKey: {
  148. win : 'Ctrl-w|Ctrl-q',
  149. mac : 'Command-w|Command-q'
  150. },
  151. exec: function(editor) {
  152. self.doCancel();
  153. }
  154. });
  155. editor.resize();
  156. dfrd.resolve(editor);
  157. };
  158. // init & start
  159. init();
  160. return dfrd;
  161. },
  162. close : function(textarea, instance) {
  163. if (instance) {
  164. instance.destroy();
  165. $(textarea).show();
  166. }
  167. },
  168. save : function(textarea, instance) {
  169. instance && $(textarea).data('ace') && (textarea.value = instance.session.getValue());
  170. },
  171. focus : function(textarea, instance) {
  172. instance && $(textarea).data('ace') && instance.focus();
  173. },
  174. resize : function(textarea, instance, e, data) {
  175. instance && instance.resize();
  176. }
  177. }
  178. ]
  179. },
  180. quicklook : {
  181. // to enable preview with Google Docs Viewer
  182. googleDocsMimes : ['application/pdf', 'image/tiff', 'application/vnd.ms-office', 'application/msword', 'application/vnd.ms-word', 'application/vnd.ms-excel', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']
  183. }
  184. }
  185. });
  186. };
  187. </script>

小结

总体来说个人使用还是非常不错的,当然对于一些成熟的网盘系统还是有一些差距。

源码:https://gitee.com/52itstyle/spring-boot-CloudDisk

SpringBoot开发案例之打造私有云网盘的更多相关文章

  1. SpringBoot开发案例之打造十万博文Web篇

    前言 通过 Python 爬取十万博文之后,最重要的是要让互联网用户访问到,那么如何做呢? 选型 从后台框架.前端模板.数据库连接池.缓存.代理服务.限流等组件多个维度选型. 后台框架 SpringB ...

  2. FreeNAS插件打造ownCloud私有云网盘

    ownCloud 是一个自由开源的个人云存储解决方案,可以自由获取无需付费,但用户需要自行架设服务器,好在FreeNAS可以通过插件轻松的构建ownCloud服务器. ownCloud 分为服务器端和 ...

  3. KODExplorer可道云-轻松搭建属于自己/团队的私有云网盘服务

    如今国内各大网盘关停的也快差不多,百度网盘限速严重.国外大牌的如 Dropbox 或 Google Drive又在长城之外,在各种VPN都被封禁的大背景下,科学上网也困难重重,麻烦到要死.那么,除了购 ...

  4. SpringBoot开发案例之多任务并行+线程池处理

    前言 前几篇文章着重介绍了后端服务数据库和多线程并行处理优化,并示例了改造前后的伪代码逻辑.当然了,优化是无止境的,前人栽树后人乘凉.作为我们开发者来说,既然站在了巨人的肩膀上,就要写出更加优化的程序 ...

  5. SpringBoot开发案例从0到1构建分布式秒杀系统

    前言 ​最近,被推送了不少秒杀架构的文章,忙里偷闲自己也总结了一下互联网平台秒杀架构设计,当然也借鉴了不少同学的思路.俗话说,脱离案例讲架构都是耍流氓,最终使用SpringBoot模拟实现了部分秒杀场 ...

  6. SpringBoot开发案例之整合Activiti工作流引擎

    前言 JBPM是目前市场上主流开源工作引擎之一,在创建者Tom Baeyens离开JBoss后,JBPM的下一个版本jBPM5完全放弃了jBPM4的基础代码,基于Drools Flow重头来过,目前官 ...

  7. SpringBoot开发案例之整合Dubbo分布式服务

    前言 在 SpringBoot 很火热的时候,阿里巴巴的分布式框架 Dubbo 不知是处于什么考虑,在停更N年之后终于进行维护了.在之前的微服务中,使用的是当当维护的版本 Dubbox,整合方式也是使 ...

  8. SpringBoot开发案例之整合Kafka实现消息队列

    前言 最近在做一款秒杀的案例,涉及到了同步锁.数据库锁.分布式锁.进程内队列以及分布式消息队列,这里对SpringBoot集成Kafka实现消息队列做一个简单的记录. Kafka简介 Kafka是由A ...

  9. 利用可道云kodexplorer在树莓派raspbian上搭建私有云网盘

    可道云kodexplorer是一款开源私有云系统,类似于owncloud,Dropbox.SkyDrive,seafile等.将可道云kodexplorer搭建在树莓派上,从而在树莓派上存储.管理家庭 ...

随机推荐

  1. 数据库优化案例——————某知名零售企业ERP系统

    写在前面 记得在自己学习数据库知识的时候特别喜欢看案例,因为优化的手段是容易掌握的,但是整体的优化思想是很难学会的.这也是为什么自己特别喜欢看案例,今天也分享自己做的优化案例. 之前分享过OA系统.H ...

  2. wtf!rds数据同步居然出问题了--菜鸟db的数据修复历程

    由于一次上线操作的数据变更太多,导致执行时间很长! 由于做手动主从关系,所以操作落在了主库上. 由于主从关系不是对整个库的操作,所以在有表新增的地方,添加了dts新的同步关系. db变更完成后,就发布 ...

  3. Java数据结构和算法 - 堆

    堆的介绍 Q: 什么是堆? A: 这里的“堆”是指一种特殊的二叉树,不要和Java.C/C++等编程语言里的“堆”混淆,后者指的是程序员用new能得到的计算机内存的可用部分 A: 堆是有如下特点的二叉 ...

  4. springboot打包不同环境配置与shell脚本部署

    本篇和大家分享的是springboot打包并结合shell脚本命令部署,重点在分享一个shell程序启动工具,希望能便利工作: profiles指定不同环境的配置 maven-assembly-plu ...

  5. 【Python篇】---Python3.5在Centoos的安装教程--超实用

    一.前述 Python3在公司用的还是比较多的,但一般Centoos默认是python2的环境.所以本文就python3的安装做个总结. 二.具体 1.查看python版本python 命令即可 2. ...

  6. property相关补充

    # Author : Kelvin # Date : 2019/1/25 15:20 class Foo: def __init__(self): self.original_price = 100 ...

  7. Linux文本三剑客超详细教程---grep、sed、awk

    awk.grep.sed是linux操作文本的三大利器,合称文本三剑客,也是必须掌握的linux命令之一.三者的功能都是处理文本,但侧重点各不相同,其中属awk功能最强大,但也最复杂.grep更适合单 ...

  8. Maven-常用插件

    罗列笔者认为比较有用的一些maven打包插件,方便后续查阅 spring-boot-maven-plugin springboot自带的maven插件,可用于简单的JAR/WAR方式打包,官方地址为h ...

  9. 【带着canvas去流浪(6)】绘制雷达图

    目录 一. 任务说明 二. 重点提示 三. 示例代码 示例代码托管在:http://www.github.com/dashnowords/blogs 博客园地址:<大史住在大前端>原创博文 ...

  10. 杭电ACM2019--数列有序!

    数列有序! Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submi ...