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

  • 2019 年 12 月 9 日
  • 笔记

前言

最近在做工作流的事情,正好有个需求,要添加一个附件上传的功能,曾找过不少上传插件,都不是特别满意。无意中发现一个很好用的开源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