BootStrap fileinput文件上传组件实例代码以及遇到的问题
最近用到图片文件上传的功能,对比之后本人决定选用bootstrap fileinput控件,本篇介绍如何使用bootstrap fileinput.js,file input等插件,该插件功能强大,样式非常美观,并且支持上传文件预览,ajax同步或异步上传,拖曳文件上传等炫酷的功能,最好用的文件上传组件。说实话:本人已经是使用了一段时间才来分享给大家的。非常实用、好用。
bootstrap-fileinput是一款非常优秀的HTML5文件上传插件,支持文件预览、多选等一系列特性。一款非常优秀的HTML5文件上传插件,支持bootstrap 3.x 和4.x版本,具有非常多的特性:多文件选择。这个插件能最简单的帮你完成文件上传功能,且使用bootstrap样式。还支持多种文件的预览,images, text, html, video, audio, flash。另外还支持ajax方式上传文件,可以看到上传进度。支持拖拽的方式添加和删除文件。
接下来我就一步一步来说怎么实现它,以及可能会遇到的一些问题。首先,大家需要的话,可以看看如下的源码以及API地址:
- bootstrap-fileinput源码:https://github.com/kartik-v/bootstrap-fileinput
- bootstrap-fileinput在线API:http://plugins.krajee.com/file-input
- bootstrap-fileinput Demo展示:http://plugins.krajee.com/file-basic-usage-demo
其次我还参考了其他一些资料,
Git下载以及参考地址:
- bootstrap-fileinput:https://github.com/kartik-v/bootstrap-fileinput
- Deme实例:http://plugins.krajee.com/file-basic-usage-demo#basic-example-1
- API详细说明:http://bootstrap-fileinput.com/
OK,看了这么多实例、Demo,我们就来用一用吧。
一、安装bootstrap fileinput
本人在项目中是使用npm进行安装的,只需要在项目的webapps下,空白地方按住shift点击右键,选择在此处打开命令查看,输入如下命令即可安装,如下图:
npm install bootstrap-fileinput
回车后安装完成即可。在对应的package.json文件和node_modules文件中就会生成版本号并下载好该控件的js和css文件;
二、实战
安装完成后,我们就可以使用了。
1、首先我们先将js和css文件引入到jsp中。
完整代码如下:
<!-- 这个文件中集中存放引入共用的css文件-->
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<script>
//'assets/bootstrap-table/src/extensions/editable/bootstrap-table-editable.js',
//'http://rawgit.com/hhurz/tableExport.jquery.plugin/master/tableExport.js',
//'http://rawgit.com/vitalets/x-editable/master/dist/bootstrap3-editable/js/bootstrap-editable.js'
var scripts = [
"<c:url value='/node_modules/bootstrap-table/dist/bootstrap-table.min.js'/>",
"<c:url value='/node_modules/bootstrap-table/dist/extensions/export/bootstrap-table-export.min.js'/>",
"<c:url value='/js/lib/bootstrap-table-treegrid.js'/>",
"<c:url value='/node_modules/bootstrap-table/dist/locale/bootstrap-table-zh-CN.min.js'/>",
"<c:url value='/node_modules/jquery-validation/dist/jquery.validate.min.js'/>",
"<c:url value='/node_modules/jquery-validation/dist/additional-methods.js'/>",
"<c:url value='/node_modules/jquery-validation/dist/localization/messages_zh.js'/>",
"<c:url value='/node_modules/sweetalert/dist/sweetalert.min.js'/>",
"<c:url value='/node_modules/x-editable/dist/bootstrap3-editable/js/bootstrap-editable.min.js'/>",
"<c:url value='/node_modules/bootstrap-table/dist/extensions/editable/bootstrap-table-editable.min.js'/>",
"<c:url value='/node_modules/node-waves/dist/waves.min.js'/>",
"<c:url value='/node_modules/jquery-confirm/dist/jquery-confirm.min.js'/>",
"<c:url value='/node_modules/bootstrap-fileinput/js/fileinput.js'/>",
"<c:url value='/node_modules/bootstrap-fileinput/js/locales/zh.js'/>",
"<c:url value='/node_modules/lightgallery/dist/js/lightgallery-all.min.js'/>",
"<c:url value='/node_modules/admin-lte/plugins/iCheck/icheck.min.js'/>",
"<c:url value='/node_modules/jquery-jsonview/dist/jquery.jsonview.js'/>",
"<c:url value='/node_modules/quill/dist/quill.js'/>",
"<c:url value='/node_modules/video.js/dist/video.min.js'/>",
"<c:url value='/node_modules/videojs-flash/dist/videojs-flash.min.js'/>",
"<c:url value='/node_modules/videojs-contrib-hls/dist/videojs-contrib-hls.min.js'/>",
],
eachSeries = function (arr, iterator, callback) {
callback = callback || function () {};
if (!arr.length) {
return callback();
}
var completed = 0;
var iterate = function () {
iterator(arr[completed], function (err) {
if (err) {
callback(err);
callback = function () {};
}
else {
completed += 1;
if (completed >= arr.length) {
callback(null);
}
else {
iterate();
}
}
});
};
iterate();
};
function getScript(url ,callback){
var head = document.getElementsByTagName('head')[0];
var script = document.createElement('script');
script.src = url;
var done = false;
// 对所有的浏览器添加头部
script.onload = script.onreadystatechange = function() {
if (!done && (!this.readyState ||
this.readyState == 'loaded' || this.readyState == 'complete')) {
done = true;
if (callback)
callback();
// 处理IE内存泄露
script.onload = script.onreadystatechange = null;
}
};
head.appendChild(script);
// 处理所有使用脚本元素注入
return undefined;
}
//动态获取css
function getCss(path){
if(!path || path.length === 0){
throw new Error('argument "path" is required !');
}
var head = document.getElementsByTagName('head')[0];
var link = document.createElement('link');
link.href = path;
link.rel = 'stylesheet';
link.type = 'text/css';
head.appendChild(link);
}
</script>
<script>
getCss("<c:url value='/css/copyImgUtl.css'/>")
getCss("<c:url value='/node_modules/bootstrap-table/dist/bootstrap-table.min.css'/>")
getCss("<c:url value='/node_modules/lightgallery/dist/css/lightgallery.min.css'/>")
getCss("<c:url value='/node_modules/bootstrap-fileinput/css/fileinput.css'/>")
getCss("<c:url value='/node_modules/admin-lte/plugins/iCheck/all.css'/>")
getCss("<c:url value='/node_modules/jquery-confirm/dist/jquery-confirm.min.css'/>")
</script>
2、在jsp中编写HTML代码块,如下图:
完整代码如下:
<div class="box box-success">
<div class="box-body">
<div class="modal-header">
<%-- <span class="modal-title" id="exampleModalLabel">
<i class="fa fa-hand-o-right" aria-hidden="true"></i>【 缩略图 】<i class="fa fa-hand-o-left" aria-hidden="true"></i>
</span>--%>
<div class="form-group">
<%-- <label for="selectedImgTypeId">模板名称</label>--%>
<select id="selectedImgTypeId" class="form-control" style="width: 100%;">
<option selected="selected" value="">请先选择图片分类名称...</option>
<c:forEach items="${mgFileTypeList}" var="fileType" varStatus="vs">
<option value='${fileType.valueUUID}'>${fileType.imgTypeName}</option>
</c:forEach>
</select>
</div>
</div>
<div class="text-muted">
<i class="fa fa-circle-o"> 图片上传的格式为:'jpg'、'jpeg'、'png'、'gif'; 文件大小不能超过5M.</i>
</div>
<div class="form-group" id="divId">
<input id="selectImgDataFileId" name="myFile" type="file" multiple>
</div>
</div>
</div>
3、在js编写控件初始化代码,以及相关事件方法。如下图所示:
<script>
//上传图片设置
var projectfileoptions = {
language : 'zh',
uploadUrl:'rest/imagesData/saveImgData',//上传后台操作的方法
allowedFileExtensions: ['jpg','jpeg','png','gif'],//限制上传文件后缀
initialCaption: '请选择上传素材',
showUpload: true, //是否显示上传按钮
showCaption: true, //是否显示标题,
showRemove:true,//是否显示文件删除/清除按钮
//maxFilePreviewSize:10240,
showBrowse:false,//是否显示浏览按钮
showPreview :true, //是否显示预览区域
//maxFileCount: 30, //表示允许同时上传的最大文件个数
enctype: 'multipart/form-data',
validateInitialCount:true,
browseOnZoneClick: true,
previewFileIcon: "<i class='glyphicon glyphicon-king'></i>",
msgFilesTooMany: "上传的图片的数量为({n}),超过允许的最大数值【{m}】!",
uploadExtraData:function (previewId,index) {//传递额外的参数
return {optionVal:$("#selectedImgTypeId option:selected").val(),optionText:$("#selectedImgTypeId option:selected").text()};
},
layoutTemplates:{
//actionDelete:'',//去掉删除按钮
actionUpload:'',//去掉上传按钮
}
};
$("#selectImgDataFileId").fileinput(projectfileoptions).on('filepreajax', function(event, data,msg, previewId, index) {
// filepreajax此事件在提交ajax请求上传之前触发
//获取下拉菜单的值
var obj = $("#selectedImgTypeId option:selected");
var option_val = obj.val();
//console.log(option_val);
if(option_val == ""){
$(".file-error-message").css("display","block");
$(".file-error-message").html('<ul><li>请先选择图片分类名称</li></ul>');
$("#divId").find(".kv-upload-progress").css("display","none");
$('#selectImgDataFileId').fileinput('enable');
return false;
}
}).on('fileuploaderror', function(event, data, msg) {
//此事件仅在ajax上传时触发,并且主要针对ajax上传时遇到上载或文件输入验证错误。此事件仅针对ajax上传并在以下情况下触发:
//当每个预览缩略图中的上传图标被点击并且文件面临上传中的验证错误时,或者
//当你将uploadAsync设置为true并且已触发批量上传时。在这种情况下,在任何所选文件面临上传验证错误后,会触发fileuploaderror事件。
var form = data.form, files = data.files, extra = data.extra,jqXHR = data.jqXHR,
response = data.response, reader = data.reader;
//console.log(jqXHR);
//console.log(jqXHR.responseJSON);
$(".file-error-message").html('<ul><li>'+jqXHR.responseJSON+'</li></ul>');
}).on("fileuploaded", function(event, data) {
//此事件仅在ajax上传且上载每个缩略图文件之后触发,即上传图片成功后的处理操作
if(data.response.data.num == 1) {
getRefreshImgDataTable();
dialogImgUploadBox.close();
}
});
/**
* 点击遮罩时重置图片信息
*/
$("#uploadImgBtnDataTarget").on('show.bs.modal',function () {
$("#selectImgFileId").fileinput('destroy');
$("#selectImgFileId").fileinput(projectfileoptions);
});
//选择下拉菜单后错误信息消失
$("#selectedImgTypeId").on("blur",function () {
var obj = $(this).attr("selected","selected");
var option_val = obj.val();
if(option_val != ""){
$(".file-error-message").css("display","none");
$(".file-error-message").empty();
$("#divId").find(".kv-upload-progress").css("display","none");
$('#selectImgDataFileId').fileinput('enable');
}
});
</script>
代码中都有注释加以说明,这里就不再一 一 进行介绍了。具体要用到的API请参考本文开头的资料或API文档;以上的js代码和HTML给出一个完整的Demo,如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%>
<script>
//上传图片设置
var projectfileoptions = {
language : 'zh',
uploadUrl:'rest/imagesData/saveImgData',//上传后台操作的方法
allowedFileExtensions: ['jpg','jpeg','png','gif'],//限制上传文件后缀
initialCaption: '请选择上传素材',
showUpload: true, //是否显示上传按钮
showCaption: true, //是否显示标题,
showRemove:true,//是否显示文件删除/清除按钮
//maxFilePreviewSize:10240,
showBrowse:false,//是否显示浏览按钮
showPreview :true, //是否显示预览区域
//maxFileCount: 30, //表示允许同时上传的最大文件个数
enctype: 'multipart/form-data',
validateInitialCount:true,
browseOnZoneClick: true,
previewFileIcon: "<i class='glyphicon glyphicon-king'></i>",
msgFilesTooMany: "上传的图片的数量为({n}),超过允许的最大数值【{m}】!",
uploadExtraData:function (previewId,index) {//传递额外的参数
return {optionVal:$("#selectedImgTypeId option:selected").val(),optionText:$("#selectedImgTypeId option:selected").text()};
},
layoutTemplates:{
//actionDelete:'',//去掉删除按钮
actionUpload:'',//去掉上传按钮
}
};
$("#selectImgDataFileId").fileinput(projectfileoptions).on('filepreajax', function(event, data,msg, previewId, index) {
// filepreajax此事件在提交ajax请求上传之前触发
//获取下拉菜单的值
var obj = $("#selectedImgTypeId option:selected");
var option_val = obj.val();
//console.log(option_val);
if(option_val == ""){
$(".file-error-message").css("display","block");
$(".file-error-message").html('<ul><li>请先选择图片分类名称</li></ul>');
$("#divId").find(".kv-upload-progress").css("display","none");
$('#selectImgDataFileId').fileinput('enable');
return false;
}
}).on('fileuploaderror', function(event, data, msg) {
//此事件仅在ajax上传时触发,并且主要针对ajax上传时遇到上载或文件输入验证错误。此事件仅针对ajax上传并在以下情况下触发:
//当每个预览缩略图中的上传图标被点击并且文件面临上传中的验证错误时,或者
//当你将uploadAsync设置为true并且已触发批量上传时。在这种情况下,在任何所选文件面临上传验证错误后,会触发fileuploaderror事件。
var form = data.form, files = data.files, extra = data.extra,jqXHR = data.jqXHR,
response = data.response, reader = data.reader;
//console.log(jqXHR);
//console.log(jqXHR.responseJSON);
$(".file-error-message").html('<ul><li>'+jqXHR.responseJSON+'</li></ul>');
}).on("fileuploaded", function(event, data) {
//此事件仅在ajax上传且上载每个缩略图文件之后触发,即上传图片成功后的处理操作
if(data.response.data.num == 1) {
getRefreshImgDataTable();
dialogImgUploadBox.close();
}
});
/**
* 点击遮罩时重置图片信息
*/
$("#uploadImgBtnDataTarget").on('show.bs.modal',function () {
$("#selectImgFileId").fileinput('destroy');
$("#selectImgFileId").fileinput(projectfileoptions);
});
//选择下拉菜单后错误信息消失
$("#selectedImgTypeId").on("blur",function () {
var obj = $(this).attr("selected","selected");
var option_val = obj.val();
if(option_val != ""){
$(".file-error-message").css("display","none");
$(".file-error-message").empty();
$("#divId").find(".kv-upload-progress").css("display","none");
$('#selectImgDataFileId').fileinput('enable');
}
});
</script>
<section class="content-header">
<h1>
图片数据
<small> >> 上传图片</small>
</h1>
<ol class="breadcrumb">
<li><a href="#"><i class="fa fa-th"></i> 图片管理</a></li>
<li class="active">图片数据</li>
<li class="active">上传图片</li>
</ol>
</section>
<section class="content">
<div class="box box-success">
<div class="box-body">
<div class="modal-header">
<%-- <span class="modal-title" id="exampleModalLabel">
<i class="fa fa-hand-o-right" aria-hidden="true"></i>【 缩略图 】<i class="fa fa-hand-o-left" aria-hidden="true"></i>
</span>--%>
<div class="form-group">
<%-- <label for="selectedImgTypeId">模板名称</label>--%>
<select id="selectedImgTypeId" class="form-control" style="width: 100%;">
<option selected="selected" value="">请先选择图片分类名称...</option>
<c:forEach items="${mgFileTypeList}" var="fileType" varStatus="vs">
<option value='${fileType.valueUUID}'>${fileType.imgTypeName}</option>
</c:forEach>
</select>
</div>
</div>
<div class="text-muted">
<i class="fa fa-circle-o"> 图片上传的格式为:'jpg'、'jpeg'、'png'、'gif'; 文件大小不能超过5M.</i>
</div>
<div class="form-group" id="divId">
<input id="selectImgDataFileId" name="myFile" type="file" multiple>
</div>
</div>
</div>
</section>
4、特别说明,见下图:
代码就是通过 data.jqXHR.responseJSON 进行获取。
$(".file-error-message").html('<ul><li>'+jqXHR.responseJSON+'</li></ul>');
其中file-error-message是FileInput控件命名的class属性名称,我是直接拿来就用,主要是将默认的错误信息替换为我自己的错误信息或后台返回的错误信息。
5、我们看一下后台代码的上传图片代码方法,完整方法代码如下:
/**
* 保存上传图片
* @param request
* @param response
* @param multipartFiles
* @return
*/
@RequestMapping(value = "/saveImgData", method = RequestMethod.POST)
@ResponseBody
public Map<String, Object> saveImgData(HttpServletRequest request, HttpServletResponse response,String optionVal,String optionText,
@RequestParam("myFile")MultipartFile[] multipartFiles) throws IOException, SQLException {
Map<String, Object> result = null;
if(null == multipartFiles && multipartFiles.length == 0){
throw new IllegalArgumentException("文件参数为空");
}
//临时文件目录
File tempFileDirectory = new File(templatePath);
if(!tempFileDirectory.exists() || !tempFileDirectory.isDirectory()){
//不存在,则创建目录
tempFileDirectory.mkdirs();
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
File templateFile = null;
List<File> fileList = new ArrayList<File>();
List<ImgDataVO> imgDataVOList = new ArrayList<ImgDataVO>();
List<String> fileNamesList = new ArrayList<String>();
for(MultipartFile multipartFile : multipartFiles){
ImgDataVO imgDataVO = new ImgDataVO();
if(null != multipartFile && null != multipartFile.getOriginalFilename()
&& !"".equals(multipartFile.getOriginalFilename().trim())
&& !"null".equals(multipartFile.getOriginalFilename().trim())){
//输出图片名称
String filename = multipartFile.getOriginalFilename();
//图片大小
long img_size = multipartFile.getSize();
long fileSizeConstant = 5242880;
if(img_size > fileSizeConstant){
throw new DataNotFoundException("图片大小不能超过5M.");
}
//格式
String format = filename.substring(filename.lastIndexOf(".")+1);
//文件格式判断工具类
if(FileTool.fileTypeAllowed(format,FileTool.ALLOWED_IMAGE)){
try {
fileNamesList.add(multipartFile.getOriginalFilename().substring(0,
multipartFile.getOriginalFilename().lastIndexOf(".")));
templateFile = File.createTempFile(UUID.randomUUID().toString(),"."+format,tempFileDirectory);
multipartFile.transferTo(templateFile);//存储为临时图片文件
} catch (IllegalStateException | IOException e) {
deleteTemplateFiles(fileList);
throw new IOException("系统忙,请稍后重试!");
}
fileList.add(templateFile);
}
}
imgDataVOList.add(imgDataVO);
}
try {
result = imgDataInfoService.insertImgDataInfo(fileNamesList,fileList,optionVal,optionText,imgDataVOList);
}catch (IOException e) {
deleteTemplateFiles(fileList);
throw new IOException("系统忙,请稍后重试!");
}catch (SQLException e) {
e.printStackTrace();
deleteTemplateFiles(fileList);
logger.error("查询出错" + e.getMessage());
throw new SQLException("系统忙,请稍后重试!");
}
deleteTemplateFiles(fileList);
return result;
}
三、效果展示
我们来看看最后的效果,初始加载如下:
点击虚线块就可以进行图片文件选择,如下:
这是我们上传就会看到图片上传的进度和成功后的效果,如下:
上传成功后,我将弹出的dialog关闭,并刷新了Table,这样就可以在table中看到我们上传的最新图片了,如下:
当然也可以选择同时上传多张图片,如下:
具体配置上传单张或是上传多张,可以参考projectfileoptions属性中的配置,可以通过直接配置属性进行控制。如maxFileCount: 30, 表示允许同时上传的最大文件个数为30张图片,具体配置请参考API详情文档http://bootstrap-fileinput.com/options.html中一一进行查找使用即可。
四、关于文件大小的限制
我是限制了文件上传的大小,如下图所示:
将上传的图片大小和限制的大小进行比较,若超过限制的大小,则爬出异常,并返回前端页面进行提示用户。后台代码如下:
前端页面效果如下:
关于Bootstrap FileInput中文API方法的整理,CSDN上也有很多,大家自己去查找搜索看看就可以了,不过不是很全。比较全的还是多多参考http://bootstrap-fileinput.com/options.html这个文档吧。
好了,就到这里了,有不足之处,多多指教,也欢迎大家一起讨论,一起学习,一起进步噢!!!