最近做项目把组件分离,之前用上了第三方组件,里面的逻辑写的不是很好,抽空时候研究了下这个组件,然后把它独立的分离出来了,虽然它是个第三方组件,但是还是有些项目中有些需求它还是实现不了,所以修改了并把它封装成了一个自己能用的组件。
一.初步认识angular-file-upload
Angular File Upload是AngularJS框架的模块。支持拖放式上传,上传进度,验证过滤器和文件上传队列。它支持本机HTML5上传,但对于较旧的浏览器,其降级为旧的iframe上传方法。可与支持标准HTML表单上传的任何服务器端平台一起使用。
二.angular-file-upload 基本API
上传的方式据我所知只有这几种:
- nv-file-select ,input file这种上传方式 ------ < input type=“file” class=‘pdf-btn’ uploader="$ctrl.fileUploader" nv-file-select>
- nv-file-drop,这个可以是任何元素,以拖拽的方式拖到这个元素的区域------ < div class=‘pdf-btn’ nv-file-drop uploader="$ctrl.fileUploader">
- nv-file-over,目前我还不知道这个是什么方式,测试无效果(有知道的同学请指教!)
uploader="$ctrl.fileUploader" 需要注意的是 $ctrl.fileUploader必须是个实例,很显然我是绑在controller上的
var fileUploader = ctrl.fileUploader = new FileUploader({
//里面写的是属性信息
});
具体有哪些属性:
1.url {String}: 上传文件的服务器路径。
2.alias {String}: 包含文件的名称,默认是file。
3.queue {Array}: 上传队列。
4.progress {Number}: 上传队列的进度,只读。
5.headers {Object}: 上传的头文件信息, 浏览器需支持HTML5。
6.formData {Array}: 与文件一起发送的表单数据。
7.filters {Array}: 在文件加入上传队列之前应用过滤器.,如果过滤器返回true则文件加入队列中。
8.autoUpload {Boolean}: 文件加入队列之后自动上传,默认是false。
9.method {String}: 请求方式,默认是POST,浏览器需支持HTML5。
10.removeAfterUpload {Boolean}: 文件上传成功之后从队列移除,默认是false。
11.isHTML5 {Boolean}: 如果浏览器支持HTML5上传则返回true,只读。
12.isUploading {Boolean}: 文件正在上传中返回true,只读。
13.queueLimit {Number} : 最大上传文件数量(预定义)。
14.withCredentials {Boolean} : 使用CORS,默认是false, 浏览器需支持HTML5。
一般 比较常用的 url,autoUpload,queueLimit(目前测试好像没用),只读的属性是改不了的。
var fileUploader = ctrl.fileUploader = new FileUploader({
//上传文件的服务器路径
url: 'http://localhost:8008/file/file/upload',
//文件加入队列之后自动上传,外面配置它是否让它自动上传
autoUpload: !!ctrl.uploadConfig.autoUpload,
queueLimit:10
});
下面说下这个实例对象有哪些方法:
1.addToQueue function(files,options,filters) {:将项目添加到队列中,其中files一个{FileList|File|HTMLInputElement},options是{Object}和filters是{String}。
2.removeFromQueue function(value) {}:从队列中移除项目,value是{FileItem}或项的索引。
3.clearQueue function() {}:从队列中删除所有元素。
4.uploadItem function(value) {}:上传的项目,其中,value是{FileItem}或项的索引。
5.cancelItem function(value) {}:取消项目,其中的上传value是{FileItem}或项目的索引。
6.uploadAll function() {}:上传队列中所有待处理的项目。
7.cancelAll function() {}:取消所有当前上传。
8.destroy function() {}:销毁一个上传器。
9.isFile function(value) {return {Boolean};}:如果值是{File},则返回true。
10.isFileLikeObject function(value) {return {Boolean};}:如果值是{FileLikeObject},则返回true 。
11.getIndexOfItem function({FileItem}) {return {Number};}:返回{FileItem}队列元素的索引。
12.getReadyItems function() {return {Array.};}:返回已准备好上传项。
13.getNotUploadedItems function() {return {Array.};}:返回队列中所有未上传项。
一般比较常用的方法 removeFromQueue,clearQueue,uploadAll,cancelAll。
再说说这个实例对象有哪些回调函数(还是以代码的形式写清楚点):
//添加单个文件失败时触发
uploader.onWhenAddingFileFailed = function(item /*{File|FileLikeObject}*/, filter, options) {
console.info('onWhenAddingFileFailed', item, filter, options);
};
//将单个文件添加到队列时触发
uploader.onAfterAddingFile = function(fileItem) {
console.info('onAfterAddingFile', fileItem);
};
//将所有拖动或选定的文件添加到队列时触发
uploader.onAfterAddingAll = function(addedFileItems) {
console.info('onAfterAddingAll', addedFileItems);
};
//在上传文件之前时触发
uploader.onBeforeUploadItem = function(item) {
console.info('onBeforeUploadItem', item);
};
//单个文件上传进度
uploader.onProgressItem = function(fileItem, progress) {
console.info('onProgressItem', fileItem, progress);
};
//总文件上传进度
uploader.onProgressAll = function(progress) {
console.info('onProgressAll', progress);
};
//单个文件上传成功时触发
uploader.onSuccessItem = function(fileItem, response, status, headers) {
console.info('onSuccessItem', fileItem, response, status, headers);
};
//单个文件上传失败时触发
uploader.onErrorItem = function(fileItem, response, status, headers) {
console.info('onErrorItem', fileItem, response, status, headers);
};
//单个文件取消上传时触发
uploader.onCancelItem = function(fileItem, response, status, headers) {
console.info('onCancelItem', fileItem, response, status, headers);
};
//单个文件上传完成时触发
uploader.onCompleteItem = function(fileItem, response, status, headers) {
console.info('onCompleteItem', fileItem, response, status, headers);
};
//所有文件上传完成时触发
uploader.onCompleteAll = function() {
console.info('onCompleteAll');
};
注释已经写的很清楚,方法都比较通俗易懂。好了说完了实例对象,下面说下单个文件的比较常用的属性,方法和回调。
属性:
1.index- {Number}序列号上传。只读。
2.progress {Number}:文件上传进度百分比。只读。
3isReady- {Boolean}文件已准备好上穿。只读。
4.isUploading {Boolean}:true是否正在上传文件。只读。
5.isUploaded {Boolean}:true如果文件已上传。只读。
6.isSuccess {Boolean}:true如果文件成功上传。只读。
7。isCancel {Boolean}:true如果上传被取消。只读。
8.ISERROR {Boolean} - true如果发生错误,而文件上传。只读。
9.uploader {Object}:Uploader对该文件的父对象的引用。只读。
方法:
1.remove function() {}:从队列中删除此文件
2.upload function() {}:上传此文件
3.cancel function() {}:取消上传该文件
回调:
1.onBeforeUpload function() {:在上传项目之前触发。
2.onProgress function(progress) {:文件上传进度。
3.onSuccess function(response, status, headers) {:成功上传文件
4.onError function(response, status, headers) {:上传错误
5.onCancel- function(response, status, headers) {取消上传时
6.onComplete function(response, status, headers) {:文件上传完成(与操作是否成功无关)
可能看到又是这么多属性和方法是不是有点懵逼了?其实很简单,之前实例对象fileUploader是操作总的,下面操作单个文件的,
我们上传文件不是有很多个文件吗?那么这些文件肯定是放到一个数组里面,[FileItem,FileItem,FileItem,FileItem,FileItem…],可以通过实例对象的queue属性获取到$ctrl.fileUploader.queue,一般我们什么时候会需要操作单个文件呢?一般主要再控制进度条的时候才用的到,看下面代码:
<ul class="progress-text" ng-repeat="item in $ctrl.fileUploader.queue" ng-show='$ctrl.fileUploader.isUploading'>
<li>{{ item.file.name }}({{ item.file.size+"KB"}})- <span ng-show='item.progress!==0 && 100 > item.progress'>{{ item.progress + '%'}} </span><span
ng-show='item.progress===100'>Complete</span><span class="close" ng-click="item.cancel()">X</span></li>
</ul>
上传中效果如下(自己写的demo,样式很随意,不要在意样式,主要看效果):
上传完的效果如下:
好了,基本的API都已经说完了,也应该有个大概的了解了!下面我具体说下我的组件是怎么分离的,首先上传组件必须是有一个上传,一个进度条,我们可能不分开他们(因为它们本来就是一起的),为了让进度条摆脱上传组件束缚最好是分开它们。
三.分析和拆分思路
1.上传
关于上传我们必须要考虑以下几点
1.上传前,我们要文件约束配置(方便我对组件的控制)
2.上传验证(验证环节,必须要有的,上传前的验证或者上传后的验证)
3.上传后,如果有验证失败给出错误提示。成功后拿到成功的数据
下面我们围绕这几点来封装它,首先看看配置信息:
ctrl.uploadConfig = {
//是否允许多选
"allowMultiple": true,
//上传文件是否可以重复
"allowRepeat": true,
//自动上传文件
"autoUpload": true,
//成功拿到数据后的事件名,通过派发事件外面可以拿到值
'dataEvent': 'parentNavItemLoaded',
//文件上传最大数量
"fileSum": 4,
//文件约束配置
"fileConstraint": {
"fromat": ".pdf",
"fileMaxSize": "4",
"width": "164",
"height": "252",
"fileNameConstraint": {
"clazz": "number",
"maximum": "4",
"minimum": "1"
}
}
};
这个就是这个上传组件配置信息,基本满足项目的需求,再看看验证:
fileUploader.filters.push({
name: "fileUploading",
fn: function (file) {
//文件格式判断 && 文件大小判断 && 文件名称范围判断
return uploadVerify.fileFormat(file, ctrl.uploadConfig.fileConstraint) && uploadVerify.fileSizeRange(file, ctrl.uploadConfig.fileConstraint) && uploadVerify.fileNameRange(file, ctrl.uploadConfig.fileConstraint.fileNameConstraint) && uploadVerify.fileQuantity(this,ctrl.uploadConfig.fileSum);
}
});
var uploadVerify = {
/** 上传之后验证
* @param {Object} file 上传文件信息
* @return Boolean 判断结果
* @description 判断文件上传总数是否超过
* -1为不限制
*/
"fileQuantity": function (fileUploader,constraint) {
//检测上传最大数量约束有没有,没有的话不验证直接通过,有的话就验证
//上传的数量和约束对比验证
var validateResult = constraint? fileUploader.queue.length <=constraint : true;
return validateResult || verifyFailed(file, "fileQuantity")
}
/**
* @param {Object} file 上传文件对象
* @param {String} failedInfo 上传文件错误信息
* @description 添加失败信息到失败文件列表中
*/
function verifyFailed(file, failedInfo) {
var failureFileList = [];
var failedInfoMap = {
"fileQuantity": "上传失败,文件上传数量已经超过" + ctrl.uploadConfig.fileSum,
};
failureFileList.push({
'message': (file.name || file.originalFileName) + failedInfoMap[failedInfo],
});
};
验证是每个文件都会验证的,所以可以根据自己的项目需求来写,uploadVerify我定义的一个对象,里面都是验证的方法, queueLimit:10表示上传的数量不能超过10,但是没有提示,所以我这里自己写了个fileQuantity验证,验证不通过的话会跑verifyFailed方法,得到具体的错误信息,然后我在所有文件加到队里里面去验证它,如果上传队列的文件大于约束,就把上传队列的多余的删除, 因为之前想在所有文件加入队里面拿到上传的总数再个约束作比较,然后从上传队列里面删除大于约束的多余的元素,后来感觉太复杂了,因为要遍历,又要去删除fileUploader.removeFromQueue(file);,太耗性能,所以还是在上面那里验证更合适,虽然每个上传文件都要验证一次,但是这里只需要判断一下数量即可,不怎么耗性能, 最后这里会派发个进度条事件(让进度条组件可以那到这个实例对象fileUploader),让进度条组件独立出来。
/**
* @param {Object} addedFileItems 所有成功上传到上传列表的文件对象
* @description 多个需上传文件,添加到上传队列中触发
*/
fileUploader.onAfterAddingAll = function (addedFileItems) {
//这里上传数量和约束进行验证
//uploadVerify.fileQuantity(addedFileItems, ctrl.uploadConfig.fileSum, fileUploader);
//派发进度条事件
eventBusService.dispatch("progressEvent", ctrl.fileUploader);
};
最后上传成功后发数据传到外面去:
/**
* @description 上传队列中,所有文件上传到服务器完成触发提
*/
fileUploader.onCompleteAll = function () {
//上传完成将所有上传成功文件通过事件派发
eventBusService.dispatch(ctrl.uploadConfig.dataEvent, successFileList);
};
2.进度条
每次上传时候都会派发事件给进度条,所以进度条组件可以取到这个上传组件的进度
exports.progressBar = {
templateUrl: "../../uploadH5Builder/progressBar/progressBar.template.html",
controller: ['eventBusService',
function progressBar(eventBusService) {
var ctrl = this;
ctrl.$onInit = function () {
//接受进度条事件动态获取进度条的值
eventBusService.addEventListener("progressEvent", function (event) {
ctrl.fileUploader = event.target;
});
};
}
]
}
这样的话,我们上传组件就和进度条组件各自独立了
<file-uploading upload-config="ctrl.uploadConfig"></file-uploading>
<progress-bar class="progress-bar-element"></progress-bar>
这篇文件主要讲我做这个项目的思路,细节部分没有仔细讲,如果有更好的方法可以分享出来,有不正的地方请指正!!好了说到这里都已经说的差不多了,欢乐的时光总是过得特别快,又到时候和大家讲拜拜!!