vue-file-upload-with-compress
Version:
upload file component for vue.js
501 lines (458 loc) • 17.7 kB
JavaScript
import _ from '../util/public.js';
import generateOptions from './generate.js';
import FileItem from './fileItem.js';
import FileAlias from './fileAlias.js';
/**
* FileUploader 类
* 图片队列保存,
* 回调函数执行
*/
class FileUploader {
constructor(options) {
_.extend(this, generateOptions(), options, {
fileIndex: 0,
isUploading: false,
failFilterIndex: -1
});
//限制图片上传数量
this.filters.push({
name: "maxLimit",
fn() {
return this.getAll().length < this.maxItems;
}
});
}
/**
* [getAll 获取所有队列文件]
* 完成用户业务组件和vue-file-upload组件的文件同步
* @return {[type]} [description]
*/
getAll() {
return this.queue;
}
/**
* [addQueue 添加到队列]
* 由组件里input发生change时,调用此函数
* @param {[FileList]} files [文件列表]
*/
addToQueue(files) {
var uploader = this;
var files = uploader._isFileList(files) ? _.toArray(files) : [files];
//file可能是File,htmlInputElement,Object
files.forEach((file) => {
var fileAlias = new FileAlias(file);
if (uploader._isValidFile(fileAlias, uploader.filters)) {
var fileItem = new FileItem(this, file, fileAlias);
uploader.queue.push(fileItem);
uploader.onAddFileSuccess(fileItem);
} else {
uploader.onAddFileFail(file, uploader.filters[uploader.failFilterIndex]);
}
});
if (uploader.autoUpload) {
uploader.uploadAll();
}
}
/**
* [uploadAll 上传所有队列里的文件]
* @return {[type]} [description]
*/
uploadAll() {
//var uploader = this;
if (!this.queue.length) return;
var fileItems = this.getNotUploadedItems();
fileItems.forEach((fileItem) => {
fileItem.onPrepareUpload();
});
fileItems.length && fileItems[0].upload();
//_.()
}
/**
* [uploadItem 开始执行上传文件]
* @param {[type]} fileItem [description]
* @return {[type]} [description]
*/
uploadItem(fileItem) {
if (this.isUploading) return;
var postMethod = _.isHTML5() ? "_xhrPost" : "_iframePost";
var fileItem = fileItem || this.getNotUploadedItems()[0];
if (fileItem) {
this.isUploading = true;
//预备上传,调整状态
fileItem.onPrepareUpload();
this[postMethod](fileItem);
}
}
/**
* [abortItem 取消上传]
* 由组件发起取消命令,执行对应的回调函数(onabort)
* @param {[type]} item [description]
* @return {[type]} [description]
*/
abortItem(item) {
var prop = _.isHTML5() ? '_xhr' : '_form';
if (item && item.isUploading) item[prop].abort();
}
/**
* [clearQueue 清空队列文件]
* @return {[type]} [description]
*/
clearQueue() {
while (this.queue.length) {
this.queue[0].remove();
}
}
/**
* [removeFromQueue 删除文件]
* @param {[type]} fileItem [description]
* @return {[type]} [description]
*/
removeFromQueue(fileItem) {
let index = this.queue.indexOf(fileItem);
if (fileItem.isUploading) fileItem.cancel();
this.queue.splice(index, 1);
fileItem.destroy();
}
/**
* 设置formData
* @param { string } key 键名
* @param { string | array | object} data 键值
*/
setFormDataItem(key, data = '') {
this.formData[key] = data;
}
/**
* [getNotUploadedItems 获取队列未上传文件列表]
* @return {[type]} [description]
*/
getNotUploadedItems() {
return this.queue.filter(item => !item.isUploaded)
}
/**
* [getNextReadyItems 获取待上传文件]
* @return {[type]} [description]
*/
getNextReadyItems() {
return this.queue.filter(item => item.isReady && !item.isUploading);
}
/**
* [onAddFileSuccess 成功添加队列回调]
* @param {[type]} fileItem [description]
* @return {[type]} [description]
*/
onAddFileSuccess(fileItem) {
}
/**
* [onAddFileFail 失败添加队列回调]
* @param {[type]} file [description]
* @param {[type]} failFilter [description]
* @return {[type]} [description]
*/
onAddFileFail(file, failFilter) {
}
/**
* [_onProgressUpload 内部方法,上传进度]
* @param {[class]} fileItem [description]
* @param {[number]} progress [进度值]
* @return {[type]} [description]
*/
_onProgressUpload(fileItem, progress) {
fileItem.onProgress(progress);
this.onProgressUpload(fileItem, progress);
}
/**
* [onProgressUpload 回调上传进度函数]
* @param {[class]} fileItem [description]
* @param {[number]} progress [description]
* @return {[type]} [description]
*/
onProgressUpload(fileItem, progress) {
}
/**
* [_onSuccessUpload 内部方法,成功上传执行函数]
* @param {[class]} fileItem [文件]
* @param {[string|xml|json]} response [服务端输出内容]
* @param {[number]} status [状态]
* @param {[string|json]} headers [头部信息]
* @return {[type]} [description]
*/
_onSuccessUpload(fileItem, response, status, headers) {
fileItem.onSuccess();
this.onSuccessUpload(fileItem, response, status, headers);
}
/**
* [onSuccessUpload 成功回调]
* @param {[type]} fileItem [description]
* @param {[type]} response [description]
* @param {[type]} status [description]
* @param {[type]} headers [description]
* @return {[type]} [description]
*/
onSuccessUpload(fileItem, response, status, headers) {
}
/**
* [_onCompleteUpload 内部方法,完成上传过程,执行函数]
* @param {[class]} fileItem [文件]
* @param {[string|xml|json]} response [服务端输出]
* @param {[number]} status [网络状态]
* @param {[string|json]} headers [头部信息]
* @return {[type]} [description]
*/
_onCompleteUpload(fileItem, response, status, headers) {
this.onCompleteUpload(fileItem, response, status, headers);
var nextItem = this.getNextReadyItems()[0];
this.isUploading = false;
if (_.isObject(nextItem)) {
nextItem.upload();
}
}
/**
* [_onCompleteUpload 完成上传回调]
* @param {[class]} fileItem [文件]
* @param {[string|xml|json]} response [服务端输出]
* @param {[number]} status [网络状态]
* @param {[string|json]} headers [头部信息]
* @return {[type]} [description]
*/
onCompleteUpload(fileItem, response, status, headers) {
}
/**
* [_onErrorUpload 内部函数,上传异常执行函数]
* @param {[class]} fileItem [文件]
* @param {[string|xml|json]} response [服务端输出]
* @param {[number]} status [网络状态]
* @param {[string|json]} headers [头部信息]
* @return {[type]} [description]
*/
_onErrorUpload(fileItem, response, status, headers) {
fileItem.onError();
this.onErrorUpload(fileItem, response, status, headers);
}
/**
* [_onErrorUpload 上传异常回调函数]
* @param {[class]} fileItem [文件]
* @param {[string|xml|json]} response [服务端输出]
* @param {[number]} status [网络状态]
* @param {[string|json]} headers [头部信息]
* @return {[type]} [description]
*/
onErrorUpload(fileItem, response, status, headers) {
}
/**
* [_onAbortUpload 内部函数,取消上传执行函数]
* @param {[class]} fileItem [文件]
* @param {[string|xml|json]} response [服务端输出]
* @param {[number]} status [网络状态]
* @param {[string|json]} headers [头部信息]
* @return {[type]} [description]
*/
_onAbortUpload(fileItem, response, status, headers) {
fileItem.onAbort()
this.onAbortUpload(fileItem, response, status, headers);
}
/**
* [onAbortUpload 取消上传回调函数]
* @param {[class]} fileItem [文件]
* @param {[string|xml|json]} response [服务端输出]
* @param {[number]} status [网络状态]
* @param {[string|json]} headers [头部信息]
* @return {[type]} [description]
*/
onAbortUpload(fileItem, response, status, headers) {
}
/**
* [_onBeforeUpload 上传之前回调函数]
* @param {[type]} fileItem [description]
* @return {[type]} [description]
*/
_onBeforeUpload(fileItem) {
fileItem.onBeforeUpload();
this.onBeforeUpload(fileItem);
}
onBeforeUpload(fileItem) {
}
/**
* [_getQueueItem 获取下一个待上传文件]
* @return {[type]} [description]
*/
_getQueueItem() {
return this.queue.shift();
}
/**
* [isFileList FileList类型判断]
* @param {[type]} fileList [description]
* @return {Boolean} [description]
*/
_isFileList(fileList) {
return fileList instanceof FileList;
}
/**
* [_isValidFile 有效的文件判断]
* @param {[File]} file [description]
* @param {[Array]} filters [description]
* @return {Boolean} true or false [description]
*/
_isValidFile(file, filters) {
this.failFilterIndex = -1;
return !filters.length ? true : filters.every((filter) => {
this.failFilterIndex++;
return filter.fn.call(this, file);
})
}
/**
* [_parseHeaders 头部信息转json格式]
* @param {[type]} headers [description]
* @return {[type]} [description]
*/
_parseHeaders(headers) {
var json = {};
if (_.isObject(headers)) return;
headers.split("\n").forEach((header) => {
let index = header.indexOf(":");
if (index > -1) {
let key = header.slice(0, index).trim();
let val = header.slice(index + 1).trim();
json[key] = json[key] ? json[key] + "," + val : val;
}
})
return json;
}
/**
* [_xhrPost 支持html5特性]
* 可以直接使用formdata,配合xmlhttprequest上传任意文件
* @param {[type]} file [description]
* @return {[type]} [description]
*/
_xhrPost(fileItem) {
var uploader = this;
//保留xhr对象,防止abort可以执行
var xhr = fileItem._xhr = new XMLHttpRequest();
var postData = new FormData();
var todo = function() {
this._onBeforeUpload(fileItem);
//追加formdata
if (!_.isEmptyObject(uploader.formData)) {
for (let key in uploader.formData) {
postData.append(key, uploader.formData[key]);
}
}
//添加上传文件
postData.append(fileItem.alias, fileItem.file, fileItem.name);
xhr.open(fileItem.method, fileItem.url, true);
//添加header信息
if (!_.isEmptyObject(uploader.headers)) {
for (let key in uploader.headers) {
xhr.setRequestHeader(key, uploader.headers[key])
}
}
//上传进度回调
xhr.upload.onprogress = (event) => {
var progress = Math.round(event.lengthComputable ? event.loaded * 100 / event.total : 0);
uploader._onProgressUpload(fileItem, progress);
};
//上传完成
xhr.onload = () => {
var headers = uploader._parseHeaders(xhr.getAllResponseHeaders());
var response = xhr.response;
var action = xhr.status == 200 ? "Success" : "Error";
var method = "_on" + action + "Upload";
uploader[method](fileItem, response, xhr.status, headers);
uploader._onCompleteUpload(fileItem, response, xhr.status, headers);
};
//上传错误
xhr.onerror = () => {
var headers = uploader._parseHeaders(xhr.getAllResponseHeaders());
var response = xhr.response;
uploader._onErrorUpload(fileItem, response, xhr.status, headers);
uploader._onCompleteUpload(fileItem, response, xhr.status, headers);
};
//上传取消
xhr.onabort = () => {
var headers = uploader._parseHeaders(xhr.getAllResponseHeaders());
var response = xhr.response;
uploader._onAbortUpload(fileItem, response, xhr.status, headers);
uploader._onCompleteUpload(fileItem, response, xhr.status, headers);
};
//返回类型设置,默认:json
xhr.responseType = uploader.responseType;
//跨域支持,默认:False
xhr.withCredentials = uploader.withCredentials;
xhr.send(postData);
}
if (!_.isEmptyObject(uploader.compress) && uploader.compress.enable) {
uploader.compress.compresor(fileItem.file, function(nfile) {
fileItem.file = nfile;
todo.apply(uploader);
});
return;
}
todo.apply(uploader);
}
/**
* [_iframePost 悲剧,竟然不支持html5]
* 只能模拟表单submit,target给iframe
* @param {[type]} file [description]
* @return {[type]} [description]
*/
_iframePost(filecontrol) {
var uploader = this;
var form = document.createElement("form");
var iframe = document.createElement("iframe");
var input = filecontrol.input;
if (filecontrol._form) {
filecontrol._form = null;
}
filecontrol._form = form;
this._onBeforeUpload(filecontrol);
input.name = filecontrol.alias;
form.style['display'] = 'none';
iframe.name = "vueUploadFile" + Date.now();
//追加formdata
if (!_.isEmptyObject(uploader.formData)) {
for (let key in uploader.formData) {
let _input = document.createElement("input");
_input.type = "hidden";
_input.name = key;
_input.value = uploader.formData[key];
form.appendChild(_input);
}
}
form.action = filecontrol.url;
form.method = filecontrol.method;
form.target = iframe.name;
form.enctype = 'multipart/form-data';
form.encoding = 'multipart/form-data';
form.abort = () => {
var xhr = { status: 0, response: null };
var headers = {};
_.off(iframe, "load");
iframe.src = "javascript:false;"
uploader._onAbortUpload(filecontrol, xhr.response, xhr.status, headers);
uploader._onCompleteUpload(filecontrol, xhr.response, xhr.status, headers);
uploader.vm.$els.fileInput.parentNode.removeChild(form);
}
uploader.vm.$els.fileInput.parentNode.insertBefore(form, uploader.vm.$els.fileInput);
form.appendChild(input.cloneNode(true));
form.appendChild(iframe);
_.on(iframe, "load", function() {
var response = '';
var status = 200;
var headers = {};
try {
response = iframe.contentDocument.body.innerHTML;
} catch (e) {
status = 500;
throw e;
}
var xhr = { response, status };
uploader._onSuccessUpload(filecontrol, xhr.response, xhr.status, headers);
uploader._onCompleteUpload(filecontrol, xhr.response, xhr.status, headers);
_.off(iframe, "load");
uploader.vm.$els.fileInput.parentNode.removeChild(form);
form = null;
iframe = null;
});
form.submit();
}
}
export default FileUploader;