SpringBoot开发案例之打造私有云网盘的实现

前言

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

环境搭建

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

项目截图

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

项目功能

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

项目配置

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

application.properties 配置:

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

ElfinderConfiguration 读取配置:

@Component
@ConfigurationProperties(prefix="file-manager") //接收application.properties中的file-manager下面的属性
public class ElfinderConfiguration {

  private Thumbnail thumbnail;

  private String command;

  private List<Node> volumes;

  private Long maxUploadSize = -1L;

  //省略部分代码
}

elfinderStorageFactory 初始化 基础Bean:

@Configuration
public class ElFinderConfig {

  @Autowired
  private ElfinderConfiguration elfinderConfiguration;

  @Bean(name = "commandFactory")
  public CommandFactory getCommandFactory() {
    CommandFactory commandFactory = new CommandFactory();
    commandFactory.setClassNamePattern(elfinderConfiguration.getCommand()+".%sCommand");
    return commandFactory;
  }

  @Bean(name = "elfinderStorageFactory")
  public ElfinderStorageFactory getElfinderStorageFactory() {
    DefaultElfinderStorageFactory elfinderStorageFactory = new DefaultElfinderStorageFactory();
    elfinderStorageFactory.setElfinderStorage(getElfinderStorage());
    return elfinderStorageFactory;
  }

  @Bean(name = "elfinderStorage")
  public ElfinderStorage getElfinderStorage() {
    DefaultElfinderStorage defaultElfinderStorage = new DefaultElfinderStorage();

    // creates thumbnail
    DefaultThumbnailWidth defaultThumbnailWidth = new DefaultThumbnailWidth();
    defaultThumbnailWidth.setThumbnailWidth(elfinderConfiguration.getThumbnail().getWidth().intValue());

    // creates volumes, volumeIds, volumeLocale and volumeSecurities
    Character defaultVolumeId = 'A';
    List<Node> elfinderConfigurationVolumes = elfinderConfiguration.getVolumes();
    List<Volume> elfinderVolumes = new ArrayList<>(elfinderConfigurationVolumes.size());
    Map<Volume, String> elfinderVolumeIds = new HashMap<>(elfinderConfigurationVolumes.size());
    Map<Volume, Locale> elfinderVolumeLocales = new HashMap<>(elfinderConfigurationVolumes.size());
    List<VolumeSecurity> elfinderVolumeSecurities = new ArrayList<>();

    // creates volumes
    for (Node elfinderConfigurationVolume : elfinderConfigurationVolumes) {

      final String alias = elfinderConfigurationVolume.getAlias();
      final String path = elfinderConfigurationVolume.getPath();
      final String source = elfinderConfigurationVolume.getSource();
      final String locale = elfinderConfigurationVolume.getLocale();
      final boolean isLocked = elfinderConfigurationVolume.getConstraint().isLocked();
      final boolean isReadable = elfinderConfigurationVolume.getConstraint().isReadable();
      final boolean isWritable = elfinderConfigurationVolume.getConstraint().isWritable();

      // creates new volume
      Volume volume = VolumeSources.of(source).newInstance(alias, path);

      elfinderVolumes.add(volume);
      elfinderVolumeIds.put(volume, Character.toString(defaultVolumeId));
      elfinderVolumeLocales.put(volume, LocaleUtils.toLocale(locale));

      // creates security constraint
      SecurityConstraint securityConstraint = new SecurityConstraint();
      securityConstraint.setLocked(isLocked);
      securityConstraint.setReadable(isReadable);
      securityConstraint.setWritable(isWritable);

      // creates volume pattern and volume security
      final String volumePattern = Character.toString(defaultVolumeId) + ElFinderConstants.ELFINDER_VOLUME_SERCURITY_REGEX;
      elfinderVolumeSecurities.add(new DefaultVolumeSecurity(volumePattern, securityConstraint));

      // prepare next volumeId character
      defaultVolumeId++;
    }

    defaultElfinderStorage.setThumbnailWidth(defaultThumbnailWidth);
    defaultElfinderStorage.setVolumes(elfinderVolumes);
    defaultElfinderStorage.setVolumeIds(elfinderVolumeIds);
    defaultElfinderStorage.setVolumeLocales(elfinderVolumeLocales);
    defaultElfinderStorage.setVolumeSecurities(elfinderVolumeSecurities);
    return defaultElfinderStorage;
  }
}

CloudDiskController 控制层实现:

@Controller
@RequestMapping("elfinder/connector")
public class CloudDiskController {

  private static final Logger logger = LoggerFactory.getLogger(CloudDiskController.class);

  public static final String OPEN_STREAM = "openStream";
  public static final String GET_PARAMETER = "getParameter";

  @Resource(name = "commandFactory")
  private ElfinderCommandFactory elfinderCommandFactory;

  @Resource(name = "elfinderStorageFactory")
  private ElfinderStorageFactory elfinderStorageFactory;

  @RequestMapping
  public void connector(HttpServletRequest request, final HttpServletResponse response) throws IOException {
    try {
      response.setCharacterEncoding("UTF-8");
      request = processMultipartContent(request);
    } catch (Exception e) {
      throw new IOException(e.getMessage());
    }

    String cmd = request.getParameter(ElFinderConstants.ELFINDER_PARAMETER_COMMAND);
    ElfinderCommand elfinderCommand = elfinderCommandFactory.get(cmd);

    try {
      final HttpServletRequest protectedRequest = request;
      elfinderCommand.execute(new ElfinderContext() {
        @Override
        public ElfinderStorageFactory getVolumeSourceFactory() {
          return elfinderStorageFactory;
        }

        @Override
        public HttpServletRequest getRequest() {
          return protectedRequest;
        }

        @Override
        public HttpServletResponse getResponse() {
          return response;
        }
      });
    } catch (Exception e) {
      logger.error("Unknown error", e);
    }
  }
  //省略部分代码
}

最后,前端页面引入:

<div id="elfinder"></div>
<script type="text/javascript" charset="utf-8">
    window.onload = function() {
      elFinder.prototype.loadCss('/elfinder/jquery-ui-1.12.1.custom/jquery-ui.css');
      $('#elfinder').elfinder({
        url : '/elfinder/connector',
        lang: 'zh_CN',
        height : window.innerHeight-20,
        commandsOptions: {
          edit: {
            editors : [
              {
                info:{
                  name:'编辑',
                  urlAsContent: false
                },
                // ACE Editor
                // `mimes` is not set for support everything kind of text file
                load : function(textarea) {
                  var self = this,
                    dfrd = $.Deferred(),
                    cdn = './elfinder/ace/',
                    init = function() {
                      if (typeof ace === 'undefined') {
                        console.log(cdn);
                        this.fm.loadScript([
                          cdn+'/ace.js',
                          cdn+'/ext-modelist.js',
                          cdn+'/ext-settings_menu.js',
                          cdn+'/ext-language_tools.js'
                        ], start);
                      } else {
                        start();
                      }
                    },
                    start = function() {
                      var editor, editorBase, mode,
                        ta = $(textarea),
                        taBase = ta.parent(),
                        dialog = taBase.parent(),
                        id = textarea.id + '_ace',
                        ext = self.file.name.replace(/^.+\.([^.]+)|(.+)$/, '$1$2').toLowerCase(),
                        // MIME/mode map
                        mimeMode = {
                          'text/x-php'       : 'php',
                          'application/x-php'    : 'php',
                          'text/html'        : 'html',
                          'application/xhtml+xml'  : 'html',
                          'text/javascript'     : 'javascript',
                          'application/javascript' : 'javascript',
                          'text/css'        : 'css',
                          'text/x-c'        : 'c_cpp',
                          'text/x-csrc'       : 'c_cpp',
                          'text/x-chdr'       : 'c_cpp',
                          'text/x-c++'       : 'c_cpp',
                          'text/x-c++src'      : 'c_cpp',
                          'text/x-c++hdr'      : 'c_cpp',
                          'text/x-shellscript'   : 'sh',
                          'application/x-csh'    : 'sh',
                          'text/x-python'      : 'python',
                          'text/x-java'       : 'java',
                          'text/x-java-source'   : 'java',
                          'text/x-ruby'       : 'ruby',
                          'text/x-perl'       : 'perl',
                          'application/x-perl'   : 'perl',
                          'text/x-sql'       : 'sql',
                          'text/xml'        : 'xml',
                          'application/docbook+xml' : 'xml',
                          'application/xml'     : 'xml'
                        };

                      // set basePath of ace
                      ace.config.set('basePath', cdn);

                      // set base height
                      taBase.height(taBase.height());

                      // detect mode
                      mode = ace.require('ace/ext/modelist').getModeForPath('/' + self.file.name).name;
                      if (mode === 'text') {
                        if (mimeMode[self.file.mime]) {
                          mode = mimeMode[self.file.mime];
                        }
                      }

                      // show MIME:mode in title bar
                      taBase.prev().children('.elfinder-dialog-title').append(' (' + self.file.mime + ' : ' + mode.split(/[\/\\]/).pop() + ')');
                      // TextArea button and Setting button
                      $('<div class="ui-dialog-buttonset"/>').css('float', 'left')
                        .append(
                          $('<button>文本框</button>')
                            .button()
                            .on('click', function(){
                              if (ta.data('ace')) {
                                ta.removeData('ace');
                                editorBase.hide();
                                ta.val(editor.session.getValue()).show().focus();
                                $(this).text('编辑器');
                              } else {
                                ta.data('ace', true);
                                editorBase.show();
                                editor.setValue(ta.hide().val(), -1);
                                editor.focus();
                                $(this).text('文本框');
                              }
                            })
                        )
                        .append(
                          $('<button>Ace editor setting</button>')
                            .button({
                              icons: {
                                primary: 'ui-icon-gear',
                                secondary: 'ui-icon-triangle-1-e'
                              },
                              text: false
                            })
                            .on('click', function(){
                              editor.showSettingsMenu();
                            })
                        )
                        .prependTo(taBase.next());

                      // Base node of Ace editor
                      editorBase = $('<div id="'+id+'" style="width:100%; height:100%;"/>').text(ta.val()).insertBefore(ta.hide());

                      // Ace editor configure
                      ta.data('ace', true);
                      editor = ace.edit(id);
                      ace.require('ace/ext/language_tools');
                      ace.require('ace/ext/settings_menu').init(editor);
                      editor.$blockScrolling = Infinity;
                      editor.setOptions({
                        theme: 'ace/theme/dawn',
                        mode: 'ace/mode/' + mode,
                        fontSize: '14px',
                        wrap: true,
                        enableBasicAutocompletion: true,
                        enableSnippets: true,
                        enableLiveAutocompletion: true
                      });
                      editor.commands.addCommand({
                        name : "saveFile",
                        bindKey: {
                          win : 'Ctrl-s',
                          mac : 'Command-s'
                        },
                        exec: function(editor) {
                          self.doSave();
                        }
                      });
                      editor.commands.addCommand({
                        name : "closeEditor",
                        bindKey: {
                          win : 'Ctrl-w|Ctrl-q',
                          mac : 'Command-w|Command-q'
                        },
                        exec: function(editor) {
                          self.doCancel();
                        }
                      });

                      editor.resize();

                      dfrd.resolve(editor);
                    };

                  // init & start
                  init();

                  return dfrd;
                },
                close : function(textarea, instance) {
                  if (instance) {
                    instance.destroy();
                    $(textarea).show();
                  }
                },
                save : function(textarea, instance) {
                  instance && $(textarea).data('ace') && (textarea.value = instance.session.getValue());
                },
                focus : function(textarea, instance) {
                  instance && $(textarea).data('ace') && instance.focus();
                },
                resize : function(textarea, instance, e, data) {
                  instance && instance.resize();
                }
              }
            ]
          },
          quicklook : {
            // to enable preview with Google Docs Viewer
            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']
          }
        }
      });
    };
  </script>

小结

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

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Spring Boot 开发私有即时通信系统(WebSocket)

    1/ 概述 利用Spring Boot作为基础框架,Spring Security作为安全框架,WebSocket作为通信框架,实现点对点聊天和群聊天. 2/ 所需依赖 Spring Boot 版本 1.5.3,使用MongoDB存储数据(非必须),Maven依赖如下: <properties> <java.version>1.8</java.version> <thymeleaf.version>3.0.0.RELEASE</thymeleaf.ve

  • SpringBoot开发案例之打造私有云网盘的实现

    前言 最近在做工作流的事情,正好有个需求,要添加一个附件上传的功能,曾找过不少上传插件,都不是特别满意.无意中发现一个很好用的开源web文件管理器插件 elfinder,功能比较完善,社区也很活跃,还方便二次开发. 环境搭建 软件 地址 SpringBoot https://spring.io/projects/spring-boot/ elFinder https://studio-42.github.io/elFinder/ 项目截图 周末抽时间做了一个简单的案例,希望对大家有所帮助,下面是

  • SpringBoot开发案例之配置Druid数据库连接池的示例

    前言 好久没有更新Spring Boot系列文章,你说忙么?也可能是,前段时间的关注点也许在其他方面了,最近项目中需要开发小程序,正好采用Spring Boot实现一个后端服务,后面会把相关的代码案例分享出来,不至于大家做小程序后端服务的时候一头雾水. 在Spring Boot下默认提供了若干种可用的连接池(dbcp,dbcp2, tomcat, hikari),当然并不支持Druid,Druid来自于阿里系的一个开源连接池,它提供了非常优秀的监控功能,下面跟大家分享一下如何与Spring Bo

  • 详解SpringBoot开发案例之整合Dubbo分布式服务

    前言 在 SpringBoot 很火热的时候,阿里巴巴的分布式框架 Dubbo 不知是处于什么考虑,在停更N年之后终于进行维护了.在之前的微服务中,使用的是当当维护的版本 Dubbox,整合方式也是使用的 xml 配置方式. 改造前 之前在 SpringBoot 中使用 Dubbox是这样的.先简单记录下版本,Dubbox-2.8.4.zkclient-0.6.zookeeper-3.4.6. 项目中引入 spring-context-dubbo.xml 配置文件如下: <?xml versio

  • SpringBoot开发案例 分布式集群共享Session详解

    前言 在分布式系统中,为了提升系统性能,通常会对单体项目进行拆分,分解成多个基于功能的微服务,如果有条件,可能还会对单个微服务进行水平扩展,保证服务高可用. 那么问题来了,如果使用传统管理 Session 的方式,我们会遇到什么样的问题? 案例 这里拿下单举例,用户小明在天猫上相中了一个的娃娃,觉得不错,果断购买,选尺寸,挑身高,然后确认选择,赶紧提交订单,然后就跳转到了登录页面!小明表示很郁闷,大写的问号??? 小明进入娃娃页面,此时请求通过代理服务发送到业务系统一. 小明选尺寸,挑身高,此操

  • 详解SpringBoot开发案例之整合定时任务(Scheduled)

    来来来小伙伴们,基于上篇的邮件服务,定时任务就不单独分项目了,天然整合进了邮件服务中. 不知道,大家在工作之中,经常会用到那些定时任务去执行特定的业务,这里列举一下我在工作中曾经使用到的几种实现. 任务介绍 Java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务.Timer的优点在于简单易用:缺点是Timer的所有任务都是由同一个线程调度的,因此所有任务都是串行执行的.同一时间只能有一个任务在执行,前一个任务的延迟或异常都将会影响到之后的任

  • Spring Boot 之HelloWorld开发案例

    1.开发工具安装 在Eclipse上安装插件:spring Tool Suite(简称STS) 2.开发实例 1).创建项目 File > New > Spring Starter Project 项目创建完成: 2).生成的源码解读 SpringBootSimpleApplication类: package com.example; import org.springframework.boot.SpringApplication; import org.springframework.bo

  • Springboot入门案例及部署项目的详细过程

    今天闲来无事就来学习一下SpringBoot框架,顺手搭了一个入门小案例. Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置.通过这种方式,Spring Boot致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者. 这篇入门案例使用的是Springboot1.5.9,可能最新的已经更简单了,创建的

  • Java之SpringBoot定时任务案例讲解

    1. SpringBoot--任务:定时任务 项目开发中经常需要执行一些定时任务,比如需要在每天凌晨的时候, 分析一次前一天的日志信息,Spring为我们提供了异步执行任务调度的方式,提供了 两个接口和两个注解,并且用corn表达式去定时. TaskScheduler //任务调度程序 TaskExecutor //任务执行者 @EnableScheduling //开启定时功能的注解,放在主入口 @Scheduled //什么时候执行 cron表达式 1.1 编写定时任务的方法 我们里面存在一

  • Android多功能时钟开发案例(实战篇)

    上一篇为大家介绍的是Android多功能时钟开发基础内容,大家可以回顾一下,Android多功能时钟开发案例(基础篇) 接下来进入实战,快点来学习吧. 一.时钟 在布局文件中我们看到,界面上只有一个TextView,这个TextView的作用就是显示一个系统的当前时间,同时这个时间还是一秒一秒跳的,要实现一秒一秒的跳就需要我们每隔一秒就要刷新一下,同时我们这里还考虑了切换到另一个Tab的时候,这个时间就不跳动了,这样就会减少这个对系统的占用,考虑到了这点我们在这里用到了Handler,通过han

  • Bootstrap前端开发案例一

    现在很多公司开发中都在使用bootstrap这个框架,bootstrap是Twitter公司的一个团队的作品,大大简化了我们的前端的开发.(后面会总结一些less的使用) 学习使用API我建议直接查看官网的API,地址:http://www.bootcss.com/ 下面是部分目标效果图: 下面我就总结一个小Demo中的技巧和原理: 第一步.http://www.bootcss.com/下载bootstrap的压缩包,新建index.html,使用sublime或其它编辑器打开index页面,解

随机推荐