JavaScript

超轻量级php框架startmvc

Bootstrap fileinput文件上传预览插件使用详解

更新时间:2020-05-11 23:30:01 作者:startmvc
介绍通过本文,你可以学习到如何封装或者开发一个前端组件,同时学习Bootstrap-fileinput组

介绍

通过本文,你可以学习到如何封装或者开发一个前端组件,同时学习Bootstrap-fileinput组件的使用,封装后使用更加简单方便。

BaseFile是AdminEAP框架中基于Bootstrap-fileinput的附件上传组件,它支持 支持多文件、在线预览、拖拽上传等功能,封装后BaseFile主要包括以下功能:

  • 弹出窗口的附件上传
  • 当前界面的附件上传
  • 显示附件明细
  • 可编辑的附件明细(删除、预览、不可新增)

关于Bootstrap-fileinput的API文档可参考http://plugins.krajee.com/file-input

本文源码已在AdminEAP框架(一个基于AdminLTE的Java开发平台)中开源,可在Github下载相关代码:

Github:https://github.com/bill1012/AdminEAP

AdminEAP官网:http://www.admineap.com

使用说明

1、初始化

如果需要在当前界面使用附件上传功能(非弹窗方式)则需要在头部引入相关的css和js文件

css文件

<link rel="stylesheet" href="./resources/common/libs/fileinput/css/fileinput.min.css" >

js文件


<script src="./resources/common/libs/fileinput/js/fileinput.js"></script>
<script src="./resources/common/libs/fileinput/js/locales/zh.js"></script>
<!--BaseFile组件-->
<script src="./resources/common/js/base-file.js"></script>

form表单上还需要配置enctype="multipart/form-data"属性

2、弹窗方式调用

BaseFile支持弹窗方式打开一个附件上传窗口,点击附件上传后,弹出窗口,上传附件关闭窗口后,上传的附件在type=file的控件回填。

在表单中点击弹窗上传附件:

弹窗上传

弹窗上传附件

上传完毕,关闭窗口,附件回填

再次打开上传附件上传窗口时,会把已有的附件回填到附件上传窗口。

配置如下:

html代码


 <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> 上传附件(Max. 10MB)
 </div>
 </div>
 <div class="form-group" id="file_container">
 <input type="file" name="file" id="attachment">
 </div> 

js代码


$("#uploadFile").file({
 title: "请上传附件",
 fileinput: {
 maxFileSize: 10240,
 maxFileCount:3
 },
 fileIdContainer:"[name='fileIds']",
 showContainer:'#attachment',
 //显示文件类型 edit=可编辑 detail=明细 默认为明细
 showType:'edit',
 //弹出窗口 执行上传附件后的回调函数(window:false不调用此方法)
 window:true,
 callback:function(fileIds,oldfileIds){
 //更新fileIds
 this.showFiles({
 fileIds:fileIds
 });
 }
 });

3、本地界面调用

本地界面调用附件上传,如下图所示:

将上传附件嵌入到当前界面方式

上传后的附件可删除、可预览

(目前图片文件可预览,其他文件不可预览,后期将集成txt/xml/html/pdf的预览功能)

配置如下:

html代码


<div class="form-group" id="file_container">
 <input type="file" name="file" id="attachment">
</div>

js代码


 $("#attachment").file({
 fileinput: {
 maxFileSize: 10240,
 maxFileCount:3
 },
 fileIdContainer:"[name='fileIds']",
 window:false
 });

4、控件参数说明

window 默认为true,弹窗方式打开

title window=true时配置,弹窗的标题,默认为“文件上传”

width window=true时配置,弹窗的宽度,默认900

winId window=true时配置,弹出的id,默认为fileWin

fileinput Bootstrap-fileinput的配置参数,会覆盖默认配置,比如允许上传哪种类型的附件allowedFileTypes,允许上传最大附件大小maxFileSize,允许上传附件的个数maxFileCount等,具体的配置参数可以查询Bootstrap-fileinput的API文档。

fileIdContainer 必须,上传后的附件id存储的位置,id以逗号分隔

showContainer window=true必须配置,文件上传后回填的区域,window=false时如不配置,则取base-file的初始对象

showType window=true配置,值为edit或者detail,edit表示回填后可对数据进行删除、预览,detail只能显示,不能删除

callback window=true配置,关闭附件上传的窗口后执行的回调函数(比如更新当前的文件列表),fileIds,oldfileIds两个参数分别是更新后文件ids和更新前的文件ids

BaseFile默认配置,BaseFile的更多实现,请查看BaseFile源码


BaseFile.prototype.default = {
 winId: "fileWin",
 width: 900,
 title: "文件上传",
 //通用文件上传界面
 url: basePath + "/file/uploader",
 //默认支持多文件上传
 multiple: true,
 //默认弹出附件上传窗口
 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, '_');
 //去除空格
 return newtext.replace(/(^\s+)|(\s+$)/g,"").replace(/\s/g,"");
 }
 }
 }

5、BaseFile控件源码


/**
 * 通用文件管理组件
 * @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;
 //extend优先级 后面的会覆盖前面的
 //alert(this.element.selector);
 //将容器ID传过去便于弹窗获取到BaseFile对象,如果页面布局不在使用jquery.load方法,则该方法会失效,因为不是一个页面了
 options.container = options.container || this.element.selector.replace("#", "");
 //初始化文件图标信息
 this.getFileIconSettings();
 this.options = $.extend(true, {}, this.default, options);
 //初始化图标信息
 this.initFileIds();

 if(this.options.window) {
 this.element.click(function () {
 $(this).data('file').openWin();
 });
 }else{
 //非弹窗形式
 if(this.options.multiple)
 this.element.attr("multiple","multiple");
 }

 //如果配置了附件编辑容器showContainer(附件列表,可单个删除),则进行初始化
 if(this.hasDisplayZone()){
 this.showFiles();
 }


 }

 BaseFile.prototype.default = {
 winId: "fileWin",
 width: 900,
 title: "文件上传",
 //通用文件上传界面
 url: basePath + "/file/uploader",
 //默认支持多文件上传
 multiple: true,
 //默认弹出附件上传窗口
 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, '_');
 //去除空格
 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);
 //深拷贝后删除属性,这样不会通过后台传送过去,防止被XSS过滤掉特殊字符
 //不需要通过参数config=传递到弹窗的参数可使用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) {
 //更新fileIds并执行回调函数
 var oldfileIds = this.options.fileIds;
 this.options.fileIds = fileIds;
 this.updateFileIds();
 if (this.options.callback) {
 this.options.callback.call(this, fileIds, oldfileIds);
 }
 }

 //调用成功后执行显示附件
 BaseFile.prototype.showFiles=function(options){
 options=options||{};
 if(!this.hasDisplayZone()){
 modals.error("请配置showContainer属性,并在容器下配置type=file的input组件");
 return;
 }
 var fileIds=options.fileIds||this.options.fileIds;
 if(!fileIds&&this.options.window){
 $(this.options.showContainer).hide();
 return;
 }
 //显示
 $(this.options.showContainer).show();
 var fileComponet=$(this.options.showContainer);
 var fileResult=this.getFileResult(fileIds),preview=fileResult.initialPreview,previewConfig=fileResult.initialPreviewConfig,self=this;
 //配置三类参数 edit=附件列表(可删除) detail=附件列表(显示) 可上传
 var defaultConfig={
 initialPreview:preview,
 initialPreviewConfig:previewConfig
 };
 var config;
 if(this.options.window){
 if(this.options.showType=="edit"){
 //全局配置->本方法默认配置->edit属性下配置->外部参数
 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("未找到showFiles中的相关配置");
 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");
 });
 }

 /**
 * 向targetIds里删除数据fileIds
 * @param fileIds
 * @param targetIds
 */
 BaseFile.prototype.deleteFileIds=function(fileIds,targetIds){
 if(!fileIds) return targetIds;
 //没有文件删除,其中必有蹊跷
 if(!targetIds){
 modals.error("没有要删除的文件,请检查是否数据没有初始化");
 return;
 }
 var fileIdArr=fileIds.split(",");
 var fresult=targetIds.split(",");
 $.each(fileIdArr,function (index,fileId){
 //存在则删除
 if($.inArray(fileId,fresult)>-1){
 fresult.splice($.inArray(fileId,fresult),1);
 }
 })
 return fresult.join();
 }

 /**
 * 向targetIds里加数据fileIds
 * @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){
 //不存在,新增
 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(){
 //不弹出窗口的话一定要绑定fileIdContainer
 if(!this.options.window){
 if(!this.options.fileIdContainer||!$(this.options.fileIdContainer)){
 modals.info("请设置fileIdContainer属性");
 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;
 };

 /**
 * 是否有显示区域
 * @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、后端源码


@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>");
 }

 //从setting.properties文件中注入文件相对目录(相对目录为显示文件)
 //@Value("${uploaderPath}") 只有配置@Config才能注入
 private static final String uploaderPath=PropertiesUtil.getValue("uploaderPath");


 /**
 * 跳转到通用文件上传窗口
 * @return
 */
 @RequestMapping(value="/uploader",method = RequestMethod.GET)
 public String uploader(String config,HttpServletRequest request){
 request.setAttribute("config",config);
 return "base/file/file_uploader";
 }


 /**
 * 通用文件上传接口,存储到固定地址,以后存储到文件服务器地址
 */
 @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();
 }

 /**
 * 多文件上传,用于uploadAsync=false(同步多文件上传使用)
 * @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<>();
 //缓存当前的文件
 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();
 //这样也可以上传同名文件了
 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);
 //将文件写入到服务器
 //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()+"上传发生异常,异常原因:"+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("文件上传失败!");
 msg.setErrorkeys(arr);
 }
 FileResult preview=getPreivewSettings(fileList,request);
 msg.setInitialPreview(preview.getInitialPreview());
 msg.setInitialPreviewConfig(preview.getInitialPreviewConfig());
 msg.setFileIds(preview.getFileIds());
 return msg;
 }

 //删除某一项文件
 @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();
 }

 /**
 * 获取字体图标map,base-file控件使用
 */
 @RequestMapping(value="/icons",method = RequestMethod.POST)
 @ResponseBody
 public Map getIcons(){
 return fileIconMap;
 }

 /**
 * 根据文件名获取icon
 * @param fileName 文件
 * @return
 */
 public String getFileIcon(String fileName){
 String ext= StrUtil.getExtName(fileName);
 return fileIconMap.get(ext)==null?fileIconMap.get("default").toString():fileIconMap.get(ext).toString();
 }

 /**
 * 根据附件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);
 }


 /**
 * 回填已有文件的缩略图
 * @param fileList 文件列表
 * @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<>();
 //缓存当前的文件
 String dirPath = request.getRealPath("/");
 String[] fileArr=new String[fileList.size()];
 int index=0;
 for (SysFile sysFile : fileList) {
 //上传后预览 TODO 该预览样式暂时不支持theme:explorer的样式,后续可以再次扩展
 //如果其他文件可预览txt、xml、html、pdf等 可在此配置
 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>");
 }
 //上传后预览配置
 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;
 }
}

总结

本文源码已在AdminEAP框架(一个基于AdminLTE的Java开发平台)中开源,可在Github下载相关代码:

Github:https://github.com/bill1012/AdminEAP

AdminEAP官网:http://www.admineap.com

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家

Bootstrap fileinput 上传 预览