introduce
Through this article, you can learn how to encapsulate or develop a front-end component, and learn how to use Bootstrap-fileinput component. It is easier and more convenient to use after encapsulation.
BaseFile is an attachment upload component based on Bootstrap-fileinput in AdminEAP framework. It supports supporting multi-file, online preview, drag-and-drop upload and other functions. After encapsulation, BaseFile mainly includes the following functions:
- Appendix upload of pop-up window
- Appendix upload of current interface
- Display attachment details
- Details of editable attachments (delete, preview, not add)
API documentation for Bootstrap-fileinput can be referred to http://plugins.krajee.com/file-input
The source code of this article has been open source in the AdminEAP framework (a Java development platform based on AdminLTE). It can be downloaded in Github.
Github: https://github.com/bill1012/AdminEAP
AdminEAP: http://www.admineap.com
Instructions
1. Initialization
If you need to use the attachment upload function (non-pop-up window mode) in the current interface, you need to introduce relevant css and js files in the header
- css file
<link rel="stylesheet" href="./resources/common/libs/fileinput/css/fileinput.min.css">
- js file
<script src="./resources/common/libs/fileinput/js/fileinput.js"></script>
<script src="./resources/common/libs/fileinput/js/locales/zh.js"></script>
<!--BaseFile assembly-->
<script src="./resources/common/js/base-file.js"></script>
The enctype="multipart/form-data" property also needs to be configured on the form
2. Bullet Window Call
BaseFile supports pop-up window to open an attachment upload window. After clicking on the attachment upload, pop-up window will appear. After the attachment upload closes the window, the uploaded attachment will be filled back in the control of type=file.
In the form, click on the bullet window to upload the attachment:
After uploading, close the window and fill in the attachment
When the upload window is opened again, the existing attachments will be filled back into the attachment upload window.
The configuration is as follows:
- html code
<input type="hidden" name="fileIds" id="fileIds">
<div class="form-group">
<div class="btn btn-default btn-file" id="uploadFile">
<i class="fa fa-paperclip"></i> Upload attachments(Max. 10MB)
</div>
</div>
<div class="form-group" id="file_container">
<input type="file" name="file" id="attachment">
</div>
- js code
$("#uploadFile").file({
title: "Please upload the attachment.",
fileinput: {
maxFileSize: 10240,
maxFileCount:3
},
fileIdContainer:"[name='fileIds']",
showContainer:'#attachment',
//Display file type edit = editable detail = detail by default
showType:'edit',
//The callback function after the pop-up window executes the upload attachment (window:false does not call this method)
window:true,
callback:function(fileIds,oldfileIds){
//Update fileIds
this.showFiles({
fileIds:fileIds
});
}
});
3. Local interface call
The local interface calls the attachment upload, as shown in the following figure:
Embedding upload attachments into the current interface
Uploaded attachments can be deleted and previewed
(At present, picture files can be previewed, other files can not be previewed, and the preview function of txt/xml/html/pdf will be integrated later.)
The configuration is as follows:
- html code
<div class="form-group" id="file_container">
<input type="file" name="file" id="attachment">
</div>
- js code
$("#attachment").file({
fileinput: {
maxFileSize: 10240,
maxFileCount:3
},
fileIdContainer:"[name='fileIds']",
window:false
});
4. Description of Control Parameters
Windows defaults to true, pop-up window opens
Configuration when title window=true, pop-up window title, default to "file upload"
width window=true configuration, bounce window width, default 900
Configuration when winId window=true, pop-up id, default fileWin
The configuration parameters of fileinput Bootstrap-fileinput override the default configuration, such as which type of attachment allowedFileTypes are allowed to upload, maxFileSize is allowed to upload the largest attachment size, maxFileCount is allowed to upload the number of attachments, etc. Specific configuration parameters can query the API documents of Bootstrap-filein.
fileIdContainer must store the location of the uploaded attachment id separated by commas
showContainer window=true must be configured. The area filled back after file upload. If window=false is not configured, the initial object of base-file is taken.
showType window=true configuration, value edit or detail,edit means that data can be deleted and previewed after backfilling. Detail can only be displayed, not deleted.
callback window=true configuration, callback functions (such as updating the current file list), fileIds, and oldfileIds are file ids after updating and file ids before updating, respectively.
BaseFile default configuration, more implementations of BaseFile, see BaseFile source code
BaseFile.prototype.default = {
winId: "fileWin",
width: 900,
title: "File upload",
//General File Upload Interface
url: basePath + "/file/uploader",
//Support multi-file upload by default
multiple: true,
//Default pop-up attachment upload window
window:true,
showType:"detail",
fileinput: {
language: 'zh',
uploadUrl: basePath + "/file/uploadMultipleFile",
deleteUrl:basePath+"/file/delete",
uploadAsync:false,
validateInitialCount:true,
overwriteInitial: false,
allowedPreviewTypes: ['image'],
previewFileIcon:'<i class="fa fa-file-o"></i>',
previewFileIconSettings: null,
slugCallback: function (text) {
var newtext=(!text||text=='') ? '' : String(text).replace(/[\-\[\]\/\{}:;#%=\(\)\*\+\?\\\^\$\|<>&"']/g, '_');
//Remove blank space
return newtext.replace(/(^\s+)|(\s+$)/g,"").replace(/\s/g,"");
}
}
}
5. BaseFile Control Source Code
/**
* General File Management Component
* @author billjiang qq:475572229
*/
(function ($, window, document, undefined) {
'use strict';
var pluginName = 'file';
$.fn[pluginName] = function (options) {
var self = $(this);
if (this == null)
return null;
var data = this.data(pluginName);
if (!data) {
data = new BaseFile(this, $.extend(true, {}, options));
self.data(pluginName, data);
}
};
var BaseFile = function (element, options) {
this.element = element;
//The extend priority will override the previous one
//alert(this.element.selector);
//Passing the container ID is easy to get the BaseFile object from the pop-up window. If the jquery.load method is not used in the page layout, the method will fail because it is not a page.
options.container = options.container || this.element.selector.replace("#", "");
//Initialize file icon information
this.getFileIconSettings();
this.options = $.extend(true, {}, this.default, options);
//Initialize icon information
this.initFileIds();
if(this.options.window) {
this.element.click(function () {
$(this).data('file').openWin();
});
}else{
//Non-elastic window form
if(this.options.multiple)
this.element.attr("multiple","multiple");
}
//If the attachment editing container showContainer is configured (attachment list can be deleted individually), initialization is performed
if(this.hasDisplayZone()){
this.showFiles();
}
}
BaseFile.prototype.default = {
winId: "fileWin",
width: 900,
title: "File upload",
//General File Upload Interface
url: basePath + "/file/uploader",
//Support multi-file upload by default
multiple: true,
//Default pop-up attachment upload window
window:true,
showType:"detail",
fileinput: {
language: 'zh',
uploadUrl: basePath + "/file/uploadMultipleFile",
deleteUrl:basePath+"/file/delete",
uploadAsync:false,
validateInitialCount:true,
overwriteInitial: false,
allowedPreviewTypes: ['image'],
previewFileIcon:'<i class="fa fa-file-o"></i>',
previewFileIconSettings: null,
slugCallback: function (text) {
var newtext=(!text||text=='') ? '' : String(text).replace(/[\-\[\]\/\{}:;#%=\(\)\*\+\?\\\^\$\|<>&"']/g, '_');
//Remove blank space
return newtext.replace(/(^\s+)|(\s+$)/g,"").replace(/\s/g,"");
}
}
}
BaseFile.prototype.getFileInputConfig=function () {
return this.options.fileinput;
}
BaseFile.prototype.getFileIconSettings = function () {
var self = this;
ajaxPost(basePath + "/file/icons", null, function (icons) {
self.previewFileIconSettings = icons;
//console.log(self.previewFileIconSettings);
})
}
BaseFile.prototype.openWin = function () {
var that = this;
var self = $.extend(true, {}, this.options);
//Delete attributes after deep copy so that they are not transmitted back-end to prevent special characters from being filtered out by XSS
//Parameters that do not need to be passed to the window through parameter config = can be deleted with delete
delete self.callback;
delete self.fileIds;
delete self.showContainer;
delete self.fileIdContainer;
delete self.fileinput;
/*console.log(this.options);
console.log("=============");
console.log(self);*/
modals.openWin({
winId: that.options.winId,
url: that.options.url + "?config=" + JSON.stringify(self),
width: that.options.width + "px",
title: that.options.title,
backdrop: "static"
});
}
BaseFile.prototype.callbackHandler = function (fileIds) {
//Update fileIds and execute callback functions
var oldfileIds = this.options.fileIds;
this.options.fileIds = fileIds;
this.updateFileIds();
if (this.options.callback) {
this.options.callback.call(this, fileIds, oldfileIds);
}
}
//Execute display attachment after successful invocation
BaseFile.prototype.showFiles=function(options){
options=options||{};
if(!this.hasDisplayZone()){
modals.error("Please configure showContainer Attributes and configure them under containers type=file Of input assembly");
return;
}
var fileIds=options.fileIds||this.options.fileIds;
if(!fileIds&&this.options.window){
$(this.options.showContainer).hide();
return;
}
//display
$(this.options.showContainer).show();
var fileComponet=$(this.options.showContainer);
var fileResult=this.getFileResult(fileIds),preview=fileResult.initialPreview,previewConfig=fileResult.initialPreviewConfig,self=this;
//Configure three parameters edit = attachment list (deletable) detail = attachment list (display) uploadable
var defaultConfig={
initialPreview:preview,
initialPreviewConfig:previewConfig
};
var config;
if(this.options.window){
if(this.options.showType=="edit"){
//Global configuration - > default configuration - > configuration under edit property - > external parameters
config=$.extend({},self.options.fileinput,defaultConfig,{
showRemove:false,
showUpload:false,
showClose:false,
showBrowse:false,
showCaption:false
},options);
}else if(this.options.showType=="detail"){
config=$.extend({},self.options.fileinput,defaultConfig,{
showRemove:false,
showUpload:false,
showClose:false,
showBrowse:false,
showCaption:false,
initialPreviewShowDelete:false
},options);
}
}else{
config=$.extend({},self.options.fileinput,defaultConfig,{
showClose:false
},options);
}
if(!config){
modals.error("not found showFiles Relevant configuration in");
return;
}
//console.log("config=========="+JSON.stringify(config));
fileComponet.fileinput('destroy');
fileComponet.fileinput(config).on("filedeleted",function (event,key) {
var newfids=self.deleteFileIds(key,self.options.fileIds);
self.options.fileIds=newfids;
self.updateFileIds();
}).on("fileuploaded",function(event,data,previewId,index){
var newfids=self.addFileIds(data.response.fileIds,self.options.fileIds);
self.options.fileIds=newfids;
self.updateFileIds();
}).on("filebatchuploadsuccess",function (event,data,previewId,index) {
var newfids=self.addFileIds(data.response.fileIds,self.options.fileIds);
self.options.fileIds=newfids;
self.updateFileIds();
}).on("filezoomhidden", function(event, params) {
$(document.body).removeClass('modal-open');
$(document.body).css("padding-right","0px");
});
}
/**
* Delete data fileIds from targetIds
* @param fileIds
* @param targetIds
*/
BaseFile.prototype.deleteFileIds=function(fileIds,targetIds){
if(!fileIds) return targetIds;
//No file deletion, there must be a problem
if(!targetIds){
modals.error("There are no files to delete. Please check if the data is not initialized.");
return;
}
var fileIdArr=fileIds.split(",");
var fresult=targetIds.split(",");
$.each(fileIdArr,function (index,fileId){
//Existence deletes
if($.inArray(fileId,fresult)>-1){
fresult.splice($.inArray(fileId,fresult),1);
}
})
return fresult.join();
}
/**
* Add data fileIds to targetIds
* @param fileIds
* @param targetIds
*/
BaseFile.prototype.addFileIds=function (fileIds,targetIds) {
if(!fileIds)return targetIds;
var fileIdArr=fileIds.split(",");
var fresult=[];
if(targetIds){
fresult=targetIds.split(",");
}
$.each(fileIdArr,function (index,fileId){
//No, NEW
if($.inArray(fileId,fresult)==-1){
fresult.push(fileId);
}
})
return fresult.join();
}
BaseFile.prototype.updateFileIds=function(){
if(this.options.fileIdContainer)
$(this.options.fileIdContainer).val(this.options.fileIds);
}
BaseFile.prototype.initFileIds=function(){
//Be sure to bind fileIdContainer if you don't pop up the window
if(!this.options.window){
if(!this.options.fileIdContainer||!$(this.options.fileIdContainer)){
modals.info("Please set up fileIdContainer attribute");
return;
}
}
if(!this.options.fileIds){
if(this.options.fileIdContainer){
this.options.fileIds=$(this.options.fileIdContainer).val();
}
}
}
BaseFile.prototype.getFileResult=function(fileIds){
var ret=null;
ajaxPost(basePath+"/file/getFiles",{fileIds:fileIds},function(result){
ret=result;
});
return ret;
};
/**
* Is there a display area?
* @returns {boolean}
*/
BaseFile.prototype.hasDisplayZone=function(){
if(!this.options.showContainer){
this.options.showContainer=this.element.selector;
}
if(!this.options.showContainer||!$(this.options.showContainer)){
return false;
}
return true;
}
})(jQuery, window, document);
6. Backend source code
package com.cnpc.framework.base.controller;
import com.cnpc.framework.base.entity.SysFile;
import com.cnpc.framework.base.entity.User;
import com.cnpc.framework.base.pojo.AvatarResult;
import com.cnpc.framework.base.pojo.FileResult;
import com.cnpc.framework.base.pojo.MarkDownResult;
import com.cnpc.framework.base.pojo.Result;
import com.cnpc.framework.base.service.UploaderService;
import com.cnpc.framework.util.SecurityUtil;
import com.cnpc.framework.utils.DateUtil;
import com.cnpc.framework.utils.FileUtil;
import com.cnpc.framework.utils.PropertiesUtil;
import com.cnpc.framework.utils.StrUtil;
import org.apache.commons.fileupload.util.Streams;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.*;
import java.util.*;
@Controller
@RequestMapping("/file")
public class UploaderController {
private static Logger logger= LoggerFactory.getLogger(UploaderController.class);
//previewFileIconSettings
public static Map fileIconMap=new HashMap();
@Resource
private UploaderService uploaderService;
static {
fileIconMap.put("doc" ,"<i class='fa fa-file-word-o text-primary'></i>");
fileIconMap.put("docx","<i class='fa fa-file-word-o text-primary'></i>");
fileIconMap.put("xls" ,"<i class='fa fa-file-excel-o text-success'></i>");
fileIconMap.put("xlsx","<i class='fa fa-file-excel-o text-success'></i>");
fileIconMap.put("ppt" ,"<i class='fa fa-file-powerpoint-o text-danger'></i>");
fileIconMap.put("pptx","<i class='fa fa-file-powerpoint-o text-danger'></i>");
fileIconMap.put("jpg" ,"<i class='fa fa-file-photo-o text-warning'></i>");
fileIconMap.put("pdf" ,"<i class='fa fa-file-pdf-o text-danger'></i>");
fileIconMap.put("zip" ,"<i class='fa fa-file-archive-o text-muted'></i>");
fileIconMap.put("rar" ,"<i class='fa fa-file-archive-o text-muted'></i>");
fileIconMap.put("default" ,"<i class='fa fa-file-o'></i>");
}
//From setting.propertiesFile injection relative directory (relative directory is display file)
//@Value("${uploaderPath}") Only configuration@Config Inability to infuse
private static final String uploaderPath=PropertiesUtil.getValue("uploaderPath");
/**
* Jump to Common File Upload Window
* @return
*/
@RequestMapping(value="/uploader",method = RequestMethod.GET)
public String uploader(String config,HttpServletRequest request){
request.setAttribute("config",config);
return "base/file/file_uploader";
}
/**
* Universal file upload interface, stored to fixed address, later stored to file server address
*/
@RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
@ResponseBody
public SysFile uploadFile(@RequestParam(value = "file", required = false) MultipartFile file,
HttpServletRequest request, HttpServletResponse response) {
//TODO dosomething
return new SysFile();
}
/**
* Multi-file upload for uploadAsync = false (synchronous multi-file upload)
* @param files
* @param request
* @param response
* @return
*/
@RequestMapping(value = "/uploadMultipleFile", method = RequestMethod.POST)
@ResponseBody
public FileResult uploadMultipleFile(@RequestParam(value = "file", required = false) MultipartFile[] files,
HttpServletRequest request, HttpServletResponse response) throws IOException {
System.out.println("the num of file:"+files.length);
FileResult msg = new FileResult();
ArrayList<Integer> arr = new ArrayList<>();
//Caching the current file
List<SysFile> fileList=new ArrayList<>();
String dirPath = request.getRealPath("/");
for (int i = 0; i < files.length; i++) {
MultipartFile file = files[i];
if (!file.isEmpty()) {
InputStream in = null;
OutputStream out = null;
try {
File dir = new File(dirPath+uploaderPath);
if (!dir.exists())
dir.mkdirs();
//So you can upload files with the same name.
String filePrefixFormat="yyyyMMddHHmmssS";
System.out.println(DateUtil.format(new Date(),filePrefixFormat));
String savedName=DateUtil.format(new Date(),filePrefixFormat)+"_"+file.getOriginalFilename();
String filePath=dir.getAbsolutePath() + File.separator + savedName;
File serverFile = new File(filePath);
//Write the file to the server
//FileUtil.copyInputStreamToFile(file.getInputStream(),serverFile);
file.transferTo(serverFile);
SysFile sysFile=new SysFile();
sysFile.setFileName(file.getOriginalFilename());
sysFile.setSavedName(savedName);
sysFile.setCreateDateTime(new Date());
sysFile.setUpdateDateTime(new Date());
sysFile.setCreateUserId(SecurityUtil.getUserId());
sysFile.setDeleted(0);
sysFile.setFileSize(file.getSize());
sysFile.setFilePath(uploaderPath+File.separator+savedName);
uploaderService.save(sysFile);
fileList.add(sysFile);
/*preview.add("<div class=\"file-preview-other\">\n" +
"<span class=\"file-other-icon\"><i class=\"fa fa-file-o text-default\"></i></span>\n" +
"</div>");*/
logger.info("Server File Location=" + serverFile.getAbsolutePath());
} catch (Exception e) {
logger.error( file.getOriginalFilename()+"Abnormal upload, abnormal reasons:"+e.getMessage());
arr.add(i);
} finally {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
}
} else {
arr.add(i);
}
}
if(arr.size() > 0) {
msg.setError("File upload failed!");
msg.setErrorkeys(arr);
}
FileResult preview=getPreivewSettings(fileList,request);
msg.setInitialPreview(preview.getInitialPreview());
msg.setInitialPreviewConfig(preview.getInitialPreviewConfig());
msg.setFileIds(preview.getFileIds());
return msg;
}
//Delete a file
@RequestMapping(value="/delete",method = RequestMethod.POST)
@ResponseBody
public Result delete(String id,HttpServletRequest request){
SysFile sysFile=uploaderService.get(SysFile.class,id);
String dirPath=request.getRealPath("/");
FileUtil.delFile(dirPath+uploaderPath+File.separator+sysFile.getSavedName());
uploaderService.delete(sysFile);
return new Result();
}
/**
* Get the Font Icon map, using the base-file control
*/
@RequestMapping(value="/icons",method = RequestMethod.POST)
@ResponseBody
public Map getIcons(){
return fileIconMap;
}
/**
* Get icon by file name
* @param fileName file
* @return
*/
public String getFileIcon(String fileName){
String ext= StrUtil.getExtName(fileName);
return fileIconMap.get(ext)==null?fileIconMap.get("default").toString():fileIconMap.get(ext).toString();
}
/**
* Obtain files based on attachment IDS
* @param fileIds
* @param request
* @return
*/
@RequestMapping(value="/getFiles",method = RequestMethod.POST)
@ResponseBody
public FileResult getFiles(String fileIds,HttpServletRequest request){
String[] fileIdArr=fileIds.split(",");
DetachedCriteria criteria=DetachedCriteria.forClass(SysFile.class);
criteria.add(Restrictions.in("id",fileIdArr));
criteria.addOrder(Order.asc("createDateTime"));
List<SysFile> fileList=uploaderService.findByCriteria(criteria);
return getPreivewSettings(fileList,request);
}
/**
* Backfill thumbnails of existing documents
* @param fileList File list
* @param request
* @return initialPreiview initialPreviewConfig fileIds
*/
public FileResult getPreivewSettings(List<SysFile> fileList,HttpServletRequest request){
FileResult fileResult=new FileResult();
List<String> previews=new ArrayList<>();
List<FileResult.PreviewConfig> previewConfigs=new ArrayList<>();
//Caching the current file
String dirPath = request.getRealPath("/");
String[] fileArr=new String[fileList.size()];
int index=0;
for (SysFile sysFile : fileList) {
//Preview TODO after uploading. This preview style does not support theme:explorer for the time being. It can be extended again later.
//If other files can be predicated txt, xml, html, pdf, etc., they can be configured here
if(FileUtil.isImage(dirPath+uploaderPath+File.separator+sysFile.getSavedName())) {
previews.add("<img src='." + sysFile.getFilePath().replace(File.separator, "/") + "' class='file-preview-image kv-preview-data' " +
"style='width:auto;height:160px' alt='" + sysFile.getFileName() + " title='" + sysFile.getFileName() + "''>");
}else{
previews.add("<div class='kv-preview-data file-preview-other-frame'><div class='file-preview-other'>" +
"<span class='file-other-icon'>"+getFileIcon(sysFile.getFileName())+"</span></div></div>");
}
//Preview configuration after upload
FileResult.PreviewConfig previewConfig=new FileResult.PreviewConfig();
previewConfig.setWidth("120px");
previewConfig.setCaption(sysFile.getFileName());
previewConfig.setKey(sysFile.getId());
// previewConfig.setUrl(request.getContextPath()+"/file/delete");
previewConfig.setExtra(new FileResult.PreviewConfig.Extra(sysFile.getId()));
previewConfig.setSize(sysFile.getFileSize());
previewConfigs.add(previewConfig);
fileArr[index++]=sysFile.getId();
}
fileResult.setInitialPreview(previews);
fileResult.setInitialPreviewConfig(previewConfigs);
fileResult.setFileIds(StrUtil.join(fileArr));
return fileResult;
}
}
summary
The source code of this article has been open source in the AdminEAP framework (a Java development platform based on AdminLTE). It can be downloaded in Github.
Github: https://github.com/bill1012/AdminEAP
AdminEAP: http://www.admineap.com