/** * Service which provides helper methods for downloading a data file from a URL, and extracting * its contents as .tar, .tar.gz, or .zip file. Note that this service depends on external * library code in the lib/ directory: * - jszip.min.js * - Blob.js * - zlib.js */ angular.module('quay').factory('DataFileService', [function() { var dataFileService = {}; dataFileService.getName_ = function(filePath) { var parts = filePath.split('/'); return parts[parts.length - 1]; }; dataFileService.tryAsZip_ = function(buf, success, failure) { var zip = null; var zipFiles = null; try { var 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': dataFileService.getName_(filePath), 'path': filePath, 'canRead': true, 'toBlob': (function(fp) { return function() { return new Blob([zip.file(fp).asArrayBuffer()]); }; }(filePath)) }); } } success(files); }; dataFileService.tryAsTarGz_ = function(buf, success, failure) { 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; } dataFileService.tryAsTar_(plain, success, failure); }; dataFileService.tryAsTar_ = function(buf, success, failure) { 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(function(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': dataFileService.getName_(path), 'path': path, 'canRead': true, 'toBlob': (function(currentFile) { return function() { return new Blob([currentFile.buffer], {type: 'application/octet-binary'}); }; }(currentFile)) }); } success(processed); break; } }); } catch (e) { failure(); } }; dataFileService.blobToString = function(blob, callback) { var reader = new FileReader(); reader.onload = function(event){ callback(reader.result); }; reader.readAsText(blob); }; dataFileService.arrayToString = function(buf, callback) { var bb = new Blob([buf], {type: 'application/octet-binary'}); var f = new FileReader(); f.onload = function(e) { callback(e.target.result); }; f.onerror = function(e) { callback(null); }; f.onabort = function(e) { callback(null); }; f.readAsText(bb); }; dataFileService.readDataArrayAsPossibleArchive = function(buf, success, failure) { dataFileService.tryAsZip_(buf, success, function() { dataFileService.tryAsTarGz_(buf, success, function() { dataFileService.tryAsTar_(buf, success, failure); }); }); }; dataFileService.downloadDataFileAsArrayBuffer = function($scope, url, progress, error, loaded) { var request = new XMLHttpRequest(); request.open('GET', url, true); request.responseType = 'arraybuffer'; request.onprogress = function(e) { $scope.$apply(function() { var percentLoaded; if (e.lengthComputable) { progress(e.loaded / e.total); } }); }; request.onerror = function() { $scope.$apply(function() { error(); }); }; request.onload = function() { if (this.status == 200) { $scope.$apply(function() { var uint8array = new Uint8Array(request.response); loaded(uint8array); }); return; } }; request.send(); }; return dataFileService; }]);