UNPKG

ng-file-upload-haikulearning

Version:

An AngularJS directive for file upload using HTML5 with FileAPI polyfill for unsupported browsers

392 lines (352 loc) 12.4 kB
/**! * AngularJS file upload directives and services. Supoorts: file upload/drop/paste, resume, cancel/abort, * progress, resize, thumbnail, preview, validation and CORS * @author Danial <danial.farid@gmail.com> * @version <%= pkg.version %> */ if (window.XMLHttpRequest && !(window.FileAPI && FileAPI.shouldLoad)) { window.XMLHttpRequest.prototype.setRequestHeader = (function (orig) { return function (header, value) { if (header === '__setXHR_') { var val = value(this); // fix for angular < 1.2.0 if (val instanceof Function) { val(this); } } else { orig.apply(this, arguments); } }; })(window.XMLHttpRequest.prototype.setRequestHeader); } var ngFileUpload = angular.module('ngFileUpload', []); ngFileUpload.version = '<%= pkg.version %>'; ngFileUpload.service('UploadBase', ['$http', '$q', '$timeout', function ($http, $q, $timeout) { var upload = this; upload.promisesCount = 0; this.isResumeSupported = function () { return window.Blob && window.Blob.prototype.slice; }; var resumeSupported = this.isResumeSupported(); function sendHttp(config) { config.method = config.method || 'POST'; config.headers = config.headers || {}; var deferred = config._deferred = config._deferred || $q.defer(); var promise = deferred.promise; function notifyProgress(e) { if (deferred.notify) { deferred.notify(e); } if (promise.progressFunc) { $timeout(function () { promise.progressFunc(e); }); } } function getNotifyEvent(n) { if (config._start != null && resumeSupported) { return { loaded: n.loaded + config._start, total: (config._file && config._file.size) || n.total, type: n.type, config: config, lengthComputable: true, target: n.target }; } else { return n; } } if (!config.disableProgress) { config.headers.__setXHR_ = function () { return function (xhr) { if (!xhr || !xhr.upload || !xhr.upload.addEventListener) return; config.__XHR = xhr; if (config.xhrFn) config.xhrFn(xhr); xhr.upload.addEventListener('progress', function (e) { e.config = config; notifyProgress(getNotifyEvent(e)); }, false); //fix for firefox not firing upload progress end, also IE8-9 xhr.upload.addEventListener('load', function (e) { if (e.lengthComputable) { e.config = config; notifyProgress(getNotifyEvent(e)); } }, false); }; }; } function uploadWithAngular() { $http(config).then(function (r) { if (resumeSupported && config._chunkSize && !config._finished && config._file) { notifyProgress({ loaded: config._end, total: config._file && config._file.size, config: config, type: 'progress' } ); upload.upload(config, true); } else { if (config._finished) delete config._finished; deferred.resolve(r); } }, function (e) { deferred.reject(e); }, function (n) { deferred.notify(n); } ); } if (!resumeSupported) { uploadWithAngular(); } else if (config._chunkSize && config._end && !config._finished) { config._start = config._end; config._end += config._chunkSize; uploadWithAngular(); } else if (config.resumeSizeUrl) { $http.get(config.resumeSizeUrl).then(function (resp) { if (config.resumeSizeResponseReader) { config._start = config.resumeSizeResponseReader(resp.data); } else { config._start = parseInt((resp.data.size == null ? resp.data : resp.data.size).toString()); } if (config._chunkSize) { config._end = config._start + config._chunkSize; } uploadWithAngular(); }, function (e) { throw e; }); } else if (config.resumeSize) { config.resumeSize().then(function (size) { config._start = size; uploadWithAngular(); }, function (e) { throw e; }); } else { if (config._chunkSize) { config._start = 0; config._end = config._start + config._chunkSize; } uploadWithAngular(); } promise.success = function (fn) { promise.then(function (response) { fn(response.data, response.status, response.headers, config); }); return promise; }; promise.error = function (fn) { promise.then(null, function (response) { fn(response.data, response.status, response.headers, config); }); return promise; }; promise.progress = function (fn) { promise.progressFunc = fn; promise.then(null, null, function (n) { fn(n); }); return promise; }; promise.abort = promise.pause = function () { if (config.__XHR) { $timeout(function () { config.__XHR.abort(); }); } return promise; }; promise.xhr = function (fn) { config.xhrFn = (function (origXhrFn) { return function () { if (origXhrFn) origXhrFn.apply(promise, arguments); fn.apply(promise, arguments); }; })(config.xhrFn); return promise; }; upload.promisesCount++; promise['finally'](function () { upload.promisesCount--; }); return promise; } this.isUploadInProgress = function () { return upload.promisesCount > 0; }; this.rename = function (file, name) { file.ngfName = name; return file; }; this.jsonBlob = function (val) { if (val != null && !angular.isString(val)) { val = JSON.stringify(val); } var blob = new window.Blob([val], {type: 'application/json'}); blob._ngfBlob = true; return blob; }; this.json = function (val) { return angular.toJson(val); }; function copy(obj) { var clone = {}; for (var key in obj) { if (obj.hasOwnProperty(key)) { clone[key] = obj[key]; } } return clone; } this.isFile = function (file) { return file != null && (file instanceof window.Blob || (file.flashId && file.name && file.size)); }; this.upload = function (config, internal) { function toResumeFile(file, formData) { if (file._ngfBlob) return file; config._file = config._file || file; if (config._start != null && resumeSupported) { if (config._end && config._end >= file.size) { config._finished = true; config._end = file.size; } var slice = file.slice(config._start, config._end || file.size); slice.name = file.name; slice.ngfName = file.ngfName; if (config._chunkSize) { formData.append('_chunkSize', config._chunkSize); formData.append('_currentChunkSize', config._end - config._start); formData.append('_chunkNumber', Math.floor(config._start / config._chunkSize)); formData.append('_totalSize', config._file.size); } return slice; } return file; } function addFieldToFormData(formData, val, key) { if (val !== undefined) { if (angular.isDate(val)) { val = val.toISOString(); } if (angular.isString(val)) { formData.append(key, val); } else if (upload.isFile(val)) { var file = toResumeFile(val, formData); var split = key.split(','); if (split[1]) { file.ngfName = split[1].replace(/^\s+|\s+$/g, ''); key = split[0]; } config._fileKey = config._fileKey || key; formData.append(key, file, file.ngfName || file.name); } else { if (angular.isObject(val)) { if (val.$$ngfCircularDetection) throw 'ngFileUpload: Circular reference in config.data. Make sure specified data for Upload.upload() has no circular reference: ' + key; val.$$ngfCircularDetection = true; try { for (var k in val) { if (val.hasOwnProperty(k) && k !== '$$ngfCircularDetection') { var objectKey = config.objectKey == null ? '[i]' : config.objectKey; if (val.length && parseInt(k) > -1) { objectKey = config.arrayKey == null ? objectKey : config.arrayKey; } addFieldToFormData(formData, val[k], key + objectKey.replace(/[ik]/g, k)); } } } finally { delete val.$$ngfCircularDetection; } } else { formData.append(key, val); } } } } function digestConfig() { config._chunkSize = upload.translateScalars(config.resumeChunkSize); config._chunkSize = config._chunkSize ? parseInt(config._chunkSize.toString()) : null; config.headers = config.headers || {}; config.headers['Content-Type'] = undefined; config.transformRequest = config.transformRequest ? (angular.isArray(config.transformRequest) ? config.transformRequest : [config.transformRequest]) : []; config.transformRequest.push(function (data) { var formData = new window.FormData(), key; data = data || config.fields || {}; if (config.file) { data.file = config.file; } for (key in data) { if (data.hasOwnProperty(key)) { var val = data[key]; if (config.formDataAppender) { config.formDataAppender(formData, key, val); } else { addFieldToFormData(formData, val, key); } } } return formData; }); } if (!internal) config = copy(config); if (!config._isDigested) { config._isDigested = true; digestConfig(); } return sendHttp(config); }; this.http = function (config) { config = copy(config); config.transformRequest = config.transformRequest || function (data) { if ((window.ArrayBuffer && data instanceof window.ArrayBuffer) || data instanceof window.Blob) { return data; } return $http.defaults.transformRequest[0].apply(this, arguments); }; config._chunkSize = upload.translateScalars(config.resumeChunkSize); config._chunkSize = config._chunkSize ? parseInt(config._chunkSize.toString()) : null; return sendHttp(config); }; this.translateScalars = function (str) { if (angular.isString(str)) { if (str.search(/kb/i) === str.length - 2) { return parseFloat(str.substring(0, str.length - 2) * 1024); } else if (str.search(/mb/i) === str.length - 2) { return parseFloat(str.substring(0, str.length - 2) * 1048576); } else if (str.search(/gb/i) === str.length - 2) { return parseFloat(str.substring(0, str.length - 2) * 1073741824); } else if (str.search(/b/i) === str.length - 1) { return parseFloat(str.substring(0, str.length - 1)); } else if (str.search(/s/i) === str.length - 1) { return parseFloat(str.substring(0, str.length - 1)); } else if (str.search(/m/i) === str.length - 1) { return parseFloat(str.substring(0, str.length - 1) * 60); } else if (str.search(/h/i) === str.length - 1) { return parseFloat(str.substring(0, str.length - 1) * 3600); } } return str; }; this.urlToBlob = function(url) { var defer = $q.defer(); $http({url: url, method: 'get', responseType: 'arraybuffer'}).then(function (resp) { var arrayBufferView = new Uint8Array(resp.data); var type = resp.headers('content-type') || 'image/WebP'; var blob = new window.Blob([arrayBufferView], {type: type}); defer.resolve(blob); //var split = type.split('[/;]'); //blob.name = url.substring(0, 150).replace(/\W+/g, '') + '.' + (split.length > 1 ? split[1] : 'jpg'); }, function (e) { defer.reject(e); }); return defer.promise; }; this.setDefaults = function (defaults) { this.defaults = defaults || {}; }; this.defaults = {}; this.version = ngFileUpload.version; } ]);