file-input 是 bootstrap 框架下的一个文件上传插件,支持基于 AJAX 和 HTML5 的多文件多线程上传、拖曳上传、文件预览等。这个插件的优点是使用简单,文件上传预览功能优雅。
安装
首先引用资源文件,可以通过自动引入和手动引入的方法。
自动安装方法
# 途径一:Bower 包管理
$ bower install bootstrap-fileinput
# 途径二:npm 安装
$ npm install bootstrap-fileinput
# 途径三:Composer 安装
$ php composer.phar require kartik-v/bootstrap-fileinput "dev-master"
手动安装方法(需事先引入 Bootstrap 和 Jquery)
先下载 bootstrap-file-input 资源文件,项目地址。最新版本支持 bootstrap 3.x 和 4.x 。
<link href="bootstrap-fileinput/css/fileinput.min.css" rel="stylesheet" />
<script src="bootstrap-fileinput/js/fileinput.min.js"></script>
<!-- 中文支持引入 -->
<script src="bootstrap-fileinput/js/locales/zh.js"></script>
使用
前端表单(HTML)
<label class="form-label">上传文件</label>
<input type="file" id="upload-files" name="upload-files" multiple/>
初始化(Javascript)
<script>
$(function () {
// 初始化上传插件
$('#upload-file').fileinput({
language: 'zh',
// 上传接口
uploadUrl: 'secure/upload-uploadify-file',
// 异步上传需要携带的其他参数,比如商品id等, 可选
uploadExtraData: {},// 异步上传需要携带的其他参数,比如id等, 可选
allowedFileExtensions: ['pdf', 'doc', 'docx', 'jpg', 'ppt'],
initialCaption: "请上传PDF或Word文档",//文本框初始话value
dropZoneEnabled: false,// 禁用dropZone区域, 选择图片后才显示预览区域(推荐)
showPreview: true,//默认true,显示文件预览区域
showCaption: true,
showUpload: true,
showRemove: true,
showClose: true,
overwriteInitial: true,//新选择文件后,是否删掉初始化的文件
layoutTemplates: {
actionDelete: ''
},
browseClass: 'btn btn-sm btn-primary'
});
});
</script>
在初始化部分,uploadUrl 指定了文件上传的 POST 请求地址,这个需要自己在后端实现。这种方式是 AJAX 上传,拖曳上传功能也必须在 AJAX 方式中方为有效。file-input 另外还支持 FORM 上传,由于我使用的较少,此处不展开。
实例
以 JAVA(Spring)为例,实现一个将文件上传到又拍云的例子。
1、引用资源
<link type="text/css" rel="stylesheet" href="/res/bootstrap/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="/res/bootstrap-fileinput/css/fileinput.min.css" />
<script type="text/javascript" src="/res/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/res/bootstrap-fileinput/js/fileinput.min.js"></script>
<script type="text/javascript" src="/res/bootstrap-fileinput/js/zh.js"></script>
2、前端页面(JSP 表单)
<div class="modal fade" id="add-section-modal" tabindex="-1" role="dialog"
aria-labelledby="myLargeModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
×
</button>
<h6 class="modal-title" id="myModalLabel">添加章节</h6>
</div>
<div class="modal-body">
<form id="section-add-form" style="margin-top:40px;">
<div class="form-line form-group-id" style="display: none;">
<span class="form-label"><span class="warning-label">*</span>id:</span>
<input type="text" class="df-input-narrow" id="training-add-id" value=""
disabled="disabled">
<span class="form-message"></span>
<br>
</div>
<div class="form-line form-group" style="display: block;">
<span class="form-label"><span class="warning-label">*</span>课程名称:</span>
<input type="text" class="df-input-narrow" id="training-name" value="默认分组"
disabled="disabled">
<span class="form-message"></span>
<br>
</div>
<div class="form-line form-group add-section-name" style="display: block;">
<span class="form-label"><span class="warning-label">*</span>章节名称:</span>
<input type="text" class="df-input-narrow" id="section-name" value="">
<span class="form-message"></span>
<br>
</div>
<div class="form-line form-group add-section-desc" style="display: block;">
<span class="form-label"><span class="warning-label">*</span>章节描述:</span>
<textarea class="df-input-narrow" id="section-desc"></textarea>
<span class="form-message"></span>
<br>
</div>
<div class="form-line control-group">
<span class="form-label"><span class="warning-label">*</span>上传课件:</span>
<div class="controls file-form-line">
<input type="file" id="upload-file" name="upload-file" multiple/>
<div class="form-group " id="files">
<span class="help-inline form-message"></span>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">
关闭
</button>
<button id="add-section-btn" type="submit" class="btn btn-primary">
确定添加
</button>
</div>
</div>
</div>
</div>
3、初始化插件(JS)
<script>
$(function () {
// 初始化上传插件
$('#upload-file').fileinput({
language: 'zh',
// 上传接口
uploadUrl: 'secure/upload-uploadify-file',
// 异步上传需要携带的其他参数,比如商品id等, 可选
uploadExtraData: {},// 异步上传需要携带的其他参数,比如id等, 可选
allowedFileExtensions: ['pdf', 'doc', 'docx', 'jpg', 'ppt'],
initialCaption: "请上传PDF或Word文档",//文本框初始话value
dropZoneEnabled: false,// 禁用dropZone区域, 选择图片后才显示预览区域(推荐)
showPreview: true,//默认true,显示文件预览区域
showCaption: true,
showUpload: true,
showRemove: true,
showClose: true,
overwriteInitial: true,//新选择文件后,是否删掉初始化的文件
layoutTemplates: {
actionDelete: ''
},
browseClass: 'btn btn-sm btn-primary'
});
//上传成功后回调函数
$('#upload-file').on('fileuploaded', function (event, data, previewId, index) {
var response = data.response;
$("#files").append('<input type="hidden" class="filePaths" name="filePaths_' + index + '.filePath" value="' + response.result + '">');
});
// 上传失败后回调函数
$('#upload-file').on('fileuploaderror', function (event, data, previewId, index) {
console.log(data.response.result);
util.error("上传附件失败");
});
});
</script>
4、文件上传控制器
@Controller
public class TrainingAction {
@RequestMapping(value = "/secure/upload-uploadify-file", method = RequestMethod.POST,produces = "application/json;charset=UTF-8")
@ResponseBody
public String uploadFile(HttpServletRequest request, HttpServletResponse response) {
UserInfo userInfo = (UserInfo) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
List<String> filePathList = new ArrayList<String>();
try {
filePathList = FileUploadUtil.uploadFileToUpyun(request, response, userInfo.getUsername());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (filePathList.size() == 0) {
return "{\"result\":\""+"系统错误"+"\"}";
}
String filePath = filePathList.get(0);
filePath =filePath.replaceAll("\\\\","\\\\\\\\");
return "{\"result\":\""+filePath+"\"}";
}
}
5、文件上传实现类(上传到又拍云)
public class FileUploadUtil {
private static Log log = LogFactory.getLog(FileUploadUtil.class);
private String HMAC_SHA1_ALGORITHM = "HmacSHA1";
// 返回 List 集合
// 满足单个和多个文件上传使用
public static List<String> uploadFileToUpyun(HttpServletRequest request,
HttpServletResponse response, String username) throws FileNotFoundException {
// 初始化又拍云sdk
RestManager manager = new RestManager(Constants.BUCKET, Constants.OPERATOR, Constants.PASS);
manager.setTimeout(60);
manager.setApiDomain(RestManager.ED_AUTO);
// 创建list集合,用于接收上传文件的路径
List<String> filePathList = new ArrayList<String>();
String strPath = ",upload,exam,files,";
// 文件上传路径
String filepath = strPath.replace(',', '/');
// 转换request,解析出request中的文件
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
// 获取文件map集合
Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
String fileName = null;
// 循环遍历,取出单个文件
for (Map.Entry<String, MultipartFile> entity : fileMap.entrySet()) {
// 获取单个文件
MultipartFile mf = entity.getValue();
// 获得原始文件名
fileName = mf.getOriginalFilename();
// 截取文件类型; 这里可以根据文件类型进行判断
String fileType = fileName.substring(fileName.lastIndexOf('.'));
try {
String newFileName = MD5FileUtil.getMD5String(mf.getBytes());
String newfilepath;
newfilepath = filepath + username + "-" + newFileName + fileType;
String filepathUrl = Constants.domain + newfilepath;
log.info("start upload file: " + fileName);
Response wrResponse = manager.writeFile(newfilepath, mf.getBytes(), null);
log.info("UpYun upload response: " + wrResponse);
filePathList.add(filepathUrl);
} catch (IOException | UpException e) {
// TODO Auto-generated catch block
e.printStackTrace();
log.info("upload failed. filename: " + fileName + e.getMessage());
return null;
}
}
return filePathList;
}
}
6、实现结果
bootstrap-fileinput 支持图片、文档、压缩文件等多类型的文件上传,允许客户端上传的文件类型在初始化时通过
allowedFileExtensions: ['pdf', 'doc', 'docx', 'jpg', 'ppt']
属性配置。
关于文件上传还需要注意代码安全问题,毕竟这是服务器一个面向用户最为直接的入口,容易让人有机可乘。