前言

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

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信