Spring Boot 实战 - 打造自己的云盘 - Go语言中文社区

Spring Boot 实战 - 打造自己的云盘


最近在做工作流的事情,正好有个需求,要添加一个附件上传的功能,曾找过不少上传插件,都不是特别满意。无意中发现一个很好用的开源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.commandfile-manager.thumbnail.width=80file-manager.volumes[0].Node=file-manager.volumes[0].source=fileSystemfile-manager.volumes[0].alias=file
# 文件存放目录,可以自定义file-manager.volumes[0].path=D:/cloudFilefile-manager.volumes[0]._default=truefile-manager.volumes[0].locale=file-manager.volumes[0].constraint.locked=falsefile-manager.volumes[0].constraint.readable=truefile-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:​​​​​​​

@Configurationpublic 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

在线演示地址:https://cloud.52itstyle.vip

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/Lao_Wu66/article/details/89680620
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-08-27 14:46:00
  • 阅读 ( 1413 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢