2023-03-11
原文作者:柒's Blog 原文地址:https://blog.52itstyle.vip/archives/1304/

202303111617107281.png

前言

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

环境搭建

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

项目截图

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

202303111617112092.png

202303111617116613.png

202303111617121984.png

202303111617127565.png

202303111617132236.png

项目功能

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

项目配置

项目在第三方插件进行二次开发,基于 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

阅读全文