SpringBoot開發(fā)案例之打造私有云網(wǎng)盤的實(shí)現(xiàn)
前言
最近在做工作流的事情,正好有個(gè)需求,要添加一個(gè)附件上傳的功能,曾找過不少上傳插件,都不是特別滿意。無意中發(fā)現(xiàn)一個(gè)很好用的開源web文件管理器插件 elfinder,功能比較完善,社區(qū)也很活躍,還方便二次開發(fā)。
環(huán)境搭建
軟件 | 地址 |
---|---|
SpringBoot | https://spring.io/projects/spring-boot/ |
elFinder | https://studio-42.github.io/elFinder/ |
項(xiàng)目截圖
周末抽時(shí)間做了一個(gè)簡單的案例,希望對大家有所幫助,下面是簡單的項(xiàng)目截圖。
項(xiàng)目功能
在線新建目錄、文件、附件上傳、下載、預(yù)覽、在線打包,圖片在線裁剪、編輯,實(shí)現(xiàn)列表試圖、圖標(biāo)視圖等等一些列功能。
項(xiàng)目配置
項(xiàng)目在第三方插件進(jìn)行二次開發(fā),基于 SpringBoot 注解配置實(shí)現(xiàn)。
application.properties 配置:
# 執(zhí)行類,內(nèi)部調(diào)用,實(shí)現(xiàn)前端相關(guān)功能 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 初始化 基礎(chǔ)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 控制層實(shí)現(xiàn):
@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>
小結(jié)
總體來說個(gè)人使用還是非常不錯的,當(dāng)然對于一些成熟的網(wǎng)盤系統(tǒng)還是有一些差距。
源碼:https://gitee.com/52itstyle/spring-boot-CloudDisk
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot配置文件導(dǎo)入方法詳細(xì)講解
Spring Boot雖然是Spring的衍生物, 但默認(rèn)情況下Boot是不能直接使用Spring的配置文件的, 我們可以通過兩種方式導(dǎo)入Spring的配置2022-10-10SpringBoot接口返回結(jié)果封裝方法實(shí)例詳解
在實(shí)際項(xiàng)目中,一般會把結(jié)果放在一個(gè)封裝類中,封裝類中包含http狀態(tài)值,狀態(tài)消息,以及實(shí)際的數(shù)據(jù)。這里主要記錄兩種方式,通過實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-09-09使用restTemplate遠(yuǎn)程調(diào)controller路徑取數(shù)據(jù)
這篇文章主要介紹了使用restTemplate遠(yuǎn)程調(diào)controller路徑取數(shù)據(jù),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08Java中的System類、BigInteger類和BigDecimal類詳解
這篇文章主要介紹了Java中的System類、BigInteger類和BigDecimal類詳解,arraycopy()方法,復(fù)制數(shù)組元素,比較適合底層調(diào)用,一般使用Arrays.copyOf()完成復(fù)制數(shù)組,需要的朋友可以參考下2023-09-09Java中如何編寫一個(gè)數(shù)的n次方(冪運(yùn)算)?
本文介紹了使用pow函數(shù)和自定義for循環(huán)計(jì)算冪的O(n)時(shí)間復(fù)雜度方法,然后重點(diǎn)講解了快速冪算法的分治思想,以及從二進(jìn)制角度的解釋,包括如何通過位運(yùn)算和循環(huán)迭代實(shí)現(xiàn)高效計(jì)算,給出了Java代碼實(shí)現(xiàn)2024-07-07Java ArrayList add(int index, E element)和set(int index, E el
今天小編就為大家分享一篇關(guān)于Java ArrayList add(int index, E element)和set(int index, E element)兩個(gè)方法的說明,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-10-10SpringSecurity登錄使用JSON格式數(shù)據(jù)的方法
這篇文章主要介紹了SpringSecurity登錄使用JSON格式數(shù)據(jù)的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-02-02