import { DataFileService } from './datafile.service'; import { Injectable, Inject } from 'ng-metadata/core'; declare const JSZip: (buf: any) => void; declare const Zlib: any; declare const Untar: (uint8Array: Uint8Array) => void; @Injectable(DataFileService.name) export class DataFileServiceImpl implements DataFileService { constructor(@Inject('fileReaderFactory') private fileReaderFactory: () => FileReader) { } public blobToString(blob: Blob, callback: (result: string) => void): void { var reader: FileReader = this.fileReaderFactory(); reader.onload = (event: Event) => callback(event.target['result']); reader.onerror = (event: Event) => callback(null); reader.onabort = (event: Event) => callback(null); reader.readAsText(blob); } public arrayToString(buf: any, callback: (result: string) => void): void { const blob: Blob = new Blob([buf], {type: 'application/octet-binary'}); var reader: FileReader = this.fileReaderFactory(); reader.onload = (event: Event) => callback(event.target['result']); reader.onerror = (event: Event) => callback(null); reader.onabort = (event: Event) => callback(null); reader.readAsText(blob); } public readDataArrayAsPossibleArchive(buf: any, success: (result: any) => void, failure: (error: any) => void): void { this.tryAsZip(buf, success, () => { this.tryAsTarGz(buf, success, () => { this.tryAsTar(buf, success, failure); }); }); } public downloadDataFileAsArrayBuffer($scope: ng.IScope, url: string, progress: (percent: number) => void, error: () => void, loaded: (uint8array: Uint8Array) => void): void { var request: XMLHttpRequest = new XMLHttpRequest(); request.open('GET', url, true); request.responseType = 'arraybuffer'; request.onprogress = (e) => { $scope.$apply(() => { var percentLoaded; if (e.lengthComputable) { progress(e.loaded / e.total); } }); }; request.onerror = () => { $scope.$apply(() => { error(); }); }; request.onload = function() { if (request.status == 200) { $scope.$apply(() => { var uint8array = new Uint8Array(request.response); loaded(uint8array); }); return; } }; request.send(); } private getName(filePath: string): string { var parts: string[] = filePath.split('/'); return parts[parts.length - 1]; } private tryAsZip(buf: any, success: (result: any) => void, failure: (error?: any) => void): void { var zip = null; var zipFiles = null; try { zip = new JSZip(buf); zipFiles = zip.files; } catch (e) { failure(); return; } var files = []; for (var filePath in zipFiles) { if (zipFiles.hasOwnProperty(filePath)) { files.push({ 'name': this.getName(filePath), 'path': filePath, 'canRead': true, 'toBlob': (function(fp) { return function() { return new Blob([zip.file(fp).asArrayBuffer()]); }; }(filePath)) }); } } success(files); } private tryAsTarGz(buf: any, success: (result: any) => void, failure: (error?: any) => void): void { var gunzip = new Zlib.Gunzip(new Uint8Array(buf)); var plain = null; try { plain = gunzip.decompress(); } catch (e) { failure(); return; } if (plain.byteLength == 0) { plain = buf; } this.tryAsTar(plain, success, failure); } private tryAsTar(buf: any, success: (result: any) => void, failure: (error?: any) => void): void { var collapsePath = function(originalPath) { // Tar files can contain entries of the form './', so we need to collapse // those paths down. var parts = originalPath.split('/'); for (var i = parts.length - 1; i >= 0; i--) { var part = parts[i]; if (part == '.') { parts.splice(i, 1); } } return parts.join('/'); }; try { var handler = new Untar(new Uint8Array(buf)); handler.process((status, read, files, err) => { switch (status) { case 'error': failure(err); break; case 'done': var processed = []; for (var i = 0; i < files.length; ++i) { var currentFile = files[i]; var path = collapsePath(currentFile.meta.filename); if (path == '' || path == 'pax_global_header') { continue; } processed.push({ 'name': this.getName(path), 'path': path, 'canRead': true, 'toBlob': (function(file) { return function() { return new Blob([file.buffer], {type: 'application/octet-binary'}); }; }(currentFile)) }); } success(processed); break; } }); } catch (e) { failure(); } } }