ngx-upload-steroids
Version:
Angular 2+ File Uploader
200 lines (199 loc) • 8.93 kB
JavaScript
import { EventEmitter } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subscriber } from 'rxjs/Subscriber';
import 'rxjs/add/observable/merge';
import 'rxjs/add/observable/from';
import 'rxjs/add/operator/mergeAll';
import 'rxjs/add/operator/combineLatest';
export var UploadStatus;
(function (UploadStatus) {
UploadStatus[UploadStatus["Queue"] = 0] = "Queue";
UploadStatus[UploadStatus["Uploading"] = 1] = "Uploading";
UploadStatus[UploadStatus["Done"] = 2] = "Done";
UploadStatus[UploadStatus["Canceled"] = 3] = "Canceled";
})(UploadStatus || (UploadStatus = {}));
export function humanizeBytes(bytes) {
if (bytes === 0) {
return '0 Byte';
}
var k = 1024;
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
var i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
var NgUploaderService = (function () {
function NgUploaderService() {
this.files = [];
this.serviceEvents = new EventEmitter();
this.uploads = [];
}
NgUploaderService.prototype.handleFiles = function (files) {
var _this = this;
this.fileList = files;
this.files = [].map.call(files, function (file, i) {
var uploadFile = {
fileIndex: i,
id: _this.generateId(),
name: file.name,
size: file.size,
type: file.type,
progress: {
status: UploadStatus.Queue,
data: {
percentage: 0,
speed: null,
speedHuman: null,
startTime: null,
endTime: null
}
},
lastModifiedDate: file.lastModifiedDate
};
_this.serviceEvents.emit({ type: 'addedToQueue', file: uploadFile });
return uploadFile;
});
/*FORCING ONLY last file on the queue*/
if (this.files.length > 1) {
this.files = [this.files[this.files.length - 1]];
}
this.serviceEvents.emit({ type: 'allAddedToQueue', file: this.files[0] });
};
NgUploaderService.prototype.initInputEvents = function (input) {
var _this = this;
input.subscribe(function (event) {
switch (event.type) {
case 'uploadFile':
_this.serviceEvents.emit({ type: 'start', file: event.file });
var sub = { instance: null };
_this.uploads.push({ file: event.file, sub: sub });
sub.instance = _this.uploadFile(event.file, event).subscribe(function (data) {
_this.serviceEvents.emit(data);
});
break;
case 'uploadAll':
var concurrency = event.concurrency > 0 ? event.concurrency : Number.POSITIVE_INFINITY;
var subscriber = Subscriber.create(function (data) {
_this.serviceEvents.emit(data);
});
_this.uploads = _this.uploads.concat(_this.files.map(function (file) {
return { file: file, sub: null };
}));
var subscription = Observable.from(_this.files.map(function (file) { return _this.uploadFile(file, event); }))
.mergeAll(concurrency)
.combineLatest(function (data) { return data; })
.subscribe(subscriber);
break;
case 'cancel':
var id_1 = event.id || null;
if (!id_1) {
return;
}
var index = _this.uploads.findIndex(function (upload) { return upload.file.id === id_1; });
if (index !== -1) {
if (_this.uploads[index].sub && _this.uploads[index].sub.instance) {
_this.uploads[index].sub.instance.unsubscribe();
}
_this.serviceEvents.emit({ type: 'cancelled', file: _this.uploads[index].file });
_this.uploads[index].file.progress.status = UploadStatus.Canceled;
}
break;
case 'cancelAll':
_this.uploads.forEach(function (upload) {
upload.file.progress.status = UploadStatus.Canceled;
_this.serviceEvents.emit({ type: 'cancelled', file: upload.file });
});
break;
case 'clearAll':
_this.files = [];
_this.fileList = null;
_this.serviceEvents.emit({ type: 'clearedAll' });
break;
}
});
};
NgUploaderService.prototype.uploadFile = function (file, event) {
var _this = this;
return new Observable(function (observer) {
var url = event.url;
var method = event.method || 'POST';
var data = event.data || {};
var headers = event.headers || {};
var reader = new FileReader();
var xhr = new XMLHttpRequest();
var time = new Date().getTime();
var speed = 0;
xhr.upload.addEventListener('progress', function (e) {
if (e.lengthComputable) {
var percentage = Math.round((e.loaded * 100) / e.total);
var diff = new Date().getTime() - time;
speed = Math.round(e.loaded / diff * 1000);
file.progress = {
status: UploadStatus.Uploading,
data: {
percentage: percentage,
speed: speed,
speedHuman: humanizeBytes(speed) + "/s",
startTime: file.progress.data.startTime || new Date().getTime(),
endTime: null
}
};
observer.next({ type: 'uploading', file: file });
}
}, false);
xhr.upload.addEventListener('error', function (e) {
observer.error(e);
observer.complete();
});
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE) {
var speedAverage = Math.round(file.size / (new Date().getTime() - file.progress.data.startTime) * 1000);
file.progress = {
status: UploadStatus.Done,
data: {
percentage: 100,
speed: speedAverage,
speedHuman: humanizeBytes(speedAverage) + "/s",
startTime: file.progress.data.startTime,
endTime: new Date().getTime()
}
};
try {
file.response = JSON.parse(xhr.response);
}
catch (e) {
file.response = xhr.response;
}
observer.next({ type: 'done', file: file });
observer.complete();
}
};
xhr.open(method, url, true);
xhr.withCredentials = event.withCredentials ? true : false;
var form = new FormData();
try {
var uploadFile_1 = _this.fileList.item(file.fileIndex);
var uploadIndex = _this.uploads.findIndex(function (upload) { return upload.file.size === uploadFile_1.size; });
if (_this.uploads[uploadIndex].file.progress.status === UploadStatus.Canceled) {
observer.complete();
}
form.append(event.fieldName || 'file', uploadFile_1, uploadFile_1.name);
Object.keys(data).forEach(function (key) { return form.append(key, data[key]); });
Object.keys(headers).forEach(function (key) { return xhr.setRequestHeader(key, headers[key]); });
_this.serviceEvents.emit({ type: 'start', file: file });
xhr.send(form);
}
catch (e) {
observer.complete();
}
return function () {
xhr.abort();
reader.abort();
};
});
};
NgUploaderService.prototype.generateId = function () {
return Math.random().toString(36).substring(7);
};
return NgUploaderService;
}());
export { NgUploaderService };