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

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

声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#nhooo.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。