"use strict"; import {Observable, Subscriber} from "rxjs"; export {}; import {XMPPService} from "../connection/XMPPService"; import {RESTService} from "../connection/RESTService"; import * as crypto from "crypto"; import * as URL from "url"; import * as fs from "fs"; //const TransferPromiseQueue = require("./TransferPromiseQueue"); import {createPromiseQueue} from "../common/promiseQueue"; import {Deferred, logEntryExit, pause} from "../common/Utils"; import {ErrorManager} from "../common/ErrorManager"; //const blobUtil = require("blob-util"); //const Blob = require("blob"); import {isStarted} from "../common/Utils"; import {Logger} from "../common/Logger"; import {FileStorageService} from "./FileStorageService"; import {S2SService} from "./S2SService"; import {EventEmitter} from "events"; import {Core} from "../Core"; import {FileDescriptor} from "../common/models/FileDescriptor"; import {GenericService} from "./GenericService"; const LOG_ID = "FileServer/SVCE - "; const API_ID = "API_CALL - "; const ONE_KILOBYTE = 1024; const ONE_MEGABYTE = 1024 * 1024; const ONE_GIGABYTE = 1024 * 1024 * 1024; @logEntryExit(LOG_ID) @isStarted([]) /** * @module * @name FileStorage * @version SDKVERSION * @public * @description * This service manage files on server side
*/ class FileServer extends GenericService{ private _capabilities: any; private transferPromiseQueue: any; private _fileStorageService: FileStorageService; public ONE_KILOBYTE: any; static getClassName(){ return 'FileServer'; } getClassName(){ return FileServer.getClassName(); } static getAccessorName(){ return 'fileServer'; } getAccessorName(){ return FileServer.getAccessorName(); } constructor(_core:Core, _eventEmitter : EventEmitter, _logger : Logger, _startConfig: { start_up:boolean, optional:boolean }) { super(_logger, LOG_ID, _eventEmitter); this.setLogLevels(this); this._startConfig = _startConfig; this._eventEmitter = _eventEmitter; this._xmpp = null; this._rest = null; this._s2s = null; this._options = {}; this._useXMPP = false; this._useS2S = false; this._logger = _logger; this._capabilities = null; this.transferPromiseQueue = null; this._fileStorageService = null; this._core = _core; } get capabilities() : Promise{ let that = this; return new Promise((resolve, reject) => { if (!that._capabilities) { if (that._rest) { that._rest.getServerCapabilities().then((capabilities) => { that._capabilities = capabilities; //that.transferPromiseQueue = new TransferPromiseQueue(that._logger); resolve(this._capabilities); }).catch(() => { return reject(); }); } else { return reject(); } return; } resolve(that._capabilities); }); } start(_options) { // , _xmpp : XMPPService, _s2s : S2SService, _rest : RESTService, _fileStorageService let that = this; that.initStartDate(); return new Promise(function (resolve, reject) { try { that._xmpp = that._core._xmpp; that._rest = that._core._rest; that._options = _options; that._s2s = that._core._s2s; that._useXMPP = that._options.useXMPP; that._useS2S = that._options.useS2S; that._fileStorageService = that._core.fileStorage; that.setStarted (); resolve(undefined); } catch (err) { return reject(err); } }); } stop() { let that = this; return new Promise(function (resolve, reject) { try { that._xmpp = null; that._rest = null; that.setStopped (); resolve(undefined); } catch (err) { return reject(err); } }); } init (useRestAtStartup : boolean) { let that = this; return new Promise(async (resolve, reject)=> { if (useRestAtStartup ) { await that.capabilities.then((result)=>{ that.setInitialized(); }).catch(() => { that.setInitialized(); }); // resolve(capa); } else { that.setInitialized(); } resolve(null); }); } /** * * @private * @description * Method retrieve data from server using range request mecanism (RFC7233) * @returns {Object} structure containing the response data from server and the index * * @param {string} url (required) server url for request * @param {number} minRange (required) minimum value of range * @param {number} maxRange (required) maximum value of range * @param {number} index (required) index of the part. Used to re-assemble the data * */ getPartialDataFromServer(url: string, minRange: number, maxRange: number, index: number) { return this._rest.getPartialDataFromServer(url, minRange, maxRange, index); } getPartialBufferFromServer(url: string, minRange:number, maxRange: number, index: number) { return this._rest.getPartialBufferFromServer(url, minRange, maxRange, index); } /** * @method getBufferFromUrlWithOptimization * @description * Method creates buffer from a file retrieved from server using optimization (range request) whenever necessary * * @param {string} url (required) server url for request * @param {string} mime (required) Mime type of the blob to be created * @param {number} fileSize=0 (optional) size of file to be retrieved. Default: 0 * @param {string} fileName (optional) name of file to be downloaded * @param {string} uploadedDate * @returns {Buffer} Buffer created from data received from server * */ getBufferFromUrlWithOptimization(url: string, mime: string, fileSize: number=0, fileName: string, uploadedDate: string) { let that = this; if (fileSize === void 0) { fileSize = 0; } if (fileName === void 0) { fileName = ""; } if (uploadedDate === void 0) { uploadedDate = ""; } if (uploadedDate.length !== 0) { url += "?update=" + crypto.createHash("md5").update(uploadedDate).digest("hex"); } let _url = url.startsWith("http") ? URL.parse(url).path : url; return new Promise((resolve, reject) => { this.capabilities.then((capabilities : any) => { if (Boolean(capabilities.maxChunkSizeDownload) && fileSize !== 0 && fileSize > capabilities.maxChunkSizeDownload) { let range = capabilities.maxChunkSizeDownload; let minRange = 0; let maxRange = range - 1; let repetition = Math.ceil(fileSize / range); let bufferArray = new Array(repetition); let promiseArray = []; for (let i = 0; repetition > 0; i++, repetition--, minRange += range, maxRange += range) { promiseArray.push( this.getPartialDataFromServer(_url, minRange, maxRange, i).then((response : any ) => { bufferArray[response.index] = response.data; return (response.data); }) ); } Promise.all(promiseArray) .then( () => { let buffer = Buffer.concat(bufferArray); that._logger.log(that.INFO, LOG_ID + "(getBufferFromUrlWithOptimization) success"); resolve(buffer); }, (error) => { that._logger.log(that.ERROR, LOG_ID + "(getBufferFromUrlWithOptimization) Error." ); that._logger.log(that.INTERNALERROR, LOG_ID + "(getBufferFromUrlWithOptimization) Error : ", error); return reject(error); } ); } else { resolve(that._rest.getFileFromUrl(_url)); } }); }); } /** * @method getFileFromUrlWithOptimization * @description * Method creates buffer from a file retrieved from server using optimization (range request) whenever necessary * * @param {string} destFile * @param {string} url (required) server url for request * @param {string} mime (required) Mime type of the blob to be created * @param {number} fileSize=0 (optional) size of file to be retrieved. Default: 0 * @param {string} fileName (optional) name of file to be downloaded * @param {string} uploadedDate (optional) date of the upload * @returns {Buffer} Buffer created from data received from server * */ getFileFromUrlWithOptimization(destFile: string, url : string, mime: string, fileSize : number=0, fileName : string, uploadedDate: string) { let that = this; if (fileSize === void 0) { fileSize = 0; } if (fileName === void 0) { fileName = ""; } if (uploadedDate === void 0) { uploadedDate = ""; } if (uploadedDate.length !== 0) { url += "?update=" + crypto.createHash("md5").update(uploadedDate).digest("hex"); } let _url = url.startsWith("http") ? URL.parse(url).path : url; let stream = fs.createWriteStream(destFile, { flags: "a" }); return new Promise((resolve, reject) => { this.capabilities.then((capabilities : any) => { if (Boolean(capabilities.maxChunkSizeDownload) && fileSize !== 0 && fileSize > capabilities.maxChunkSizeDownload) { let range = capabilities.maxChunkSizeDownload; let minRange = 0; let maxRange = range - 1; let repetition = Math.ceil(fileSize / range); let blobArray = new Array(repetition); let promiseArray = []; for (let i = 0; repetition > 0; i++, repetition--, minRange += range, maxRange += range) { promiseArray.push( this.getPartialDataFromServer(_url, minRange, maxRange, i) .then((response : any)=> { blobArray[response.index] = response.data; return (response.data); }) ); } Promise.all(promiseArray) .then( () => { let buffer = Buffer.concat(blobArray); that._logger.log(that.INFO, LOG_ID + "(getFileFromUrlWithOptimization) success"); resolve(buffer); }, (error) => { that._logger.log(that.ERROR, LOG_ID + "(getFileFromUrlWithOptimization) Error."); that._logger.log(that.INTERNALERROR, LOG_ID + "(getFileFromUrlWithOptimization) Error : ", error); return reject(error); } ); } else { resolve(that._rest.getFileFromUrl(_url)); } }); }); } /*** * @private * @param fileDescriptor * @param large */ public async getBlobThumbnailFromFileDescriptor(fileDescriptor: any, large: boolean = false) { /* if (fileDescriptor.thumbnail.isThumbnailAvailable() || (fileDescriptor.isImage() && fileDescriptor.size < (20 * this.ONE_KILOBYTE)) ) { // Check if a request for this thumbnail is already lauched let existingPromise = this.thumbnailPromises[fileDescriptor.id]; if (existingPromise) { this.$log.info("[FileServerService] getBlobThumbnailFromFileDescriptor " + fileDescriptor.id + " already lauched"); return existingPromise.promise; } // Create the defered object let defered = this.$q.defer(); this.thumbnailPromises[fileDescriptor.id] = defered; // Forge the thumbnail url let url = fileDescriptor.url; if (fileDescriptor.thumbnail.isThumbnailAvailable() && fileDescriptor.size >= (20 * this.ONE_KILOBYTE)) { if (large) { url += "?thumbnail500=true"; } else { url += "?thumbnail=true"; } } else if (fileDescriptor.uploadedDate) { url += "?update=" + MD5.hexdigest(fileDescriptor.uploadedDate); } // Get the thumbnail blob this.getBlobFromUrl(url, fileDescriptor.typeMIME, fileDescriptor.size, fileDescriptor.fileName) .then((blob) => { fileDescriptor.previewBlob = blob; this.$rootScope.$broadcast("ON_FILE_TRANSFER_EVENT", { result: "success", type: "download", fileDesc: fileDescriptor}); delete this.thumbnailPromises[fileDescriptor.id]; defered.resolve(blob); }) .catch((error) => { this.$rootScope.$broadcast("ON_FILE_TRANSFER_EVENT", { result: "failure", type: "download", message: error.message, fileDesc: fileDescriptor}); delete this.thumbnailPromises[fileDescriptor.id]; defered.reject(error); }); return defered.promise; } else { return this.$q.reject(); } */ }; /** * @description * Method sends data file to server * * @private * @param {string} fileId (required) file descriptor ID of file to be sent * @param {string} filePath (required) file path to file to be sent * @param {string} mime (required) mime type of file * @returns {Promise} file descriptor data received as response from server or http error response * */ _uploadAFile(fileId : string, filePath : string, mime : string) { let that = this; return new Promise((resolve, reject) => { let fileDescriptor = that._fileStorageService.getFileDescriptorById(fileId); if (fileDescriptor) { fileDescriptor.state = "uploading"; } let stream = fs.createReadStream(filePath); //let buffer = new Buffer(ONE_MEGABYTE); /* let myWritableStreamBuffer = new streamBuffers.WritableStreamBuffer({ initialSize: (1000 * 1024), // start at 100 kilobytes. incrementAmount: (100 * 1024) // grow by 10 kilobytes each time buffer overflows. }); */ //stream.pipe(myWritableStreamBuffer); // stream.pipe(buffer) that._rest.uploadAStream(fileId, stream).then( (response) => { //let fileDescResponse = response.data.data; let newFileDescriptor = that._fileStorageService.getFileDescriptorById(fileId); if (newFileDescriptor) { newFileDescriptor.state = "uploaded"; } that._logger.log(that.INFO, LOG_ID + "(_uploadAFile) success"); // this.$rootScope.$broadcast("ON_FILE_TRANSFER_EVENT", { // result: "success", // type: "upload", // url: this.portalURL + "/" + fileId, // fileId: fileId, // mime: mime, // filename: file.name, // filesize: file.size // }); // this._fileStorageService.orderDocuments(); resolve(newFileDescriptor); }).catch( (errorResponse) => { // let error = this.errorHelperService.handleError(errorResponse); // this.$rootScope.$broadcast("ON_FILE_TRANSFER_EVENT", { // result: "failure", // type: "upload", // url: this.portalURL + "/" + fileId, // fileId: fileId, // mime: mime, // filename: file.name, // filesize: file.size // }); that._logger.log(that.ERROR, LOG_ID + "(_uploadAFile) error." ); that._logger.log(that.INTERNALERROR, LOG_ID + "(_uploadAFile) error : ", errorResponse); return reject(errorResponse); }); }); } /** * @description * Method sends data to server using range request mecanism (RFC7233) * * @private * @param {string} fileId (required) file descriptor ID of file to be sent * @param {Buffer} file (required) file to be sent * @param {number} index (required) index of the part. Used to indicate the part number to the server * @returns {Promise<{}>} file descriptor data received as response from server or http error response * */ _sendPartialDataToServer(fileId : string, file : Buffer, index : number) { let that = this; return new Promise((resolve, reject) => { that._rest.sendPartialDataToServer(fileId, file, index).then( (response : any) => { let filedescriptor = response.data; that._logger.log(that.INFO, LOG_ID + "(_sendPartialDataToServer) sendPartialDataToServer success"); resolve(filedescriptor); }, (errorResponse) => { //let error = this.errorHelperService.handleError(errorResponse); that._logger.log(that.ERROR, LOG_ID + "(_sendPartialDataToServer) Error." ); that._logger.log(that.INTERNALERROR, LOG_ID + "(_sendPartialDataToServer) Error : ", errorResponse); return reject(errorResponse); }); }); } /** * @description * Upload File ByChunk progressCallback callback is displayed as part of the Requester class. * @callback uploadAFileByChunk~progressCallback * @param {FileDescriptor} fileDescriptor */ /** * @method uploadAFileByChunk * Method sends data to server using range request mecanism (RFC7233) * * @private * @param {FileDescriptor} fileDescriptor (required) file descriptor Object of file to be sent * @param {string} filePath (required) filePath of the file to be sent // * @param {uploadAFileByChunk~progressCallback} progressCallback (required) initial size of whole file to be sent before partition * @returns {Promise<{FileDescriptor}>} file descriptor data received as response from server or http error response * */ async uploadAFileByChunk(fileDescriptor : FileDescriptor, filePath : string /*, progressCallback */) { let that = this; let promiseQueue = createPromiseQueue(that._logger); let fileStats = fs.statSync(filePath); //let range = ONE_MEGABYTE; let range = (await that.capabilities).maxChunkSizeUpload; if (range < fileStats.size) { if (fileStats.size >= 100 * range) { range = (fileStats.size / 100) + this.ONE_KILOBYTE; that._logger.log(that.DEBUG, LOG_ID + "(uploadAFileByChunk) changing chunk size: " + range); } let deferred = new Deferred(); fileDescriptor.chunkTotalNumber = Math.ceil(fileStats.size / range); fileDescriptor.chunkPerformed = 0; fileDescriptor.chunkPerformedPercent = 0; fileDescriptor.state = "uploading"; //let promiseArray = []; let fd = fs.openSync(filePath, "r+"); let partialSent = (promiseDeferred, blob, i) => { //let promiseArrayDeferred = new Deferred(); that._sendPartialDataToServer(fileDescriptor.id, blob, i) .then((response) => { fileDescriptor.chunkPerformed++; fileDescriptor.chunkPerformedPercent = 100 * fileDescriptor.chunkPerformed / fileDescriptor.chunkTotalNumber; // progressCallback(fileDescriptor); return promiseDeferred.resolve(response); }) .catch((error) => { that._logger.log(that.ERROR, LOG_ID + "(uploadAFileByChunk) error on chunk upload."); that._logger.log(that.INTERNALERROR, LOG_ID + "(uploadAFileByChunk) error on chunk upload : ", error); return promiseDeferred.reject(error); }); return promiseDeferred.promise; }; for (let i = 0, minRange = 0, maxRange = range - 1, repetition = Math.ceil(fileStats.size / range); repetition > 0; i++, repetition--, minRange += range, maxRange += range) { let max = maxRange < fileStats.size ? maxRange + 1 : fileStats.size; let sizeToRead = max - minRange; let buf = new Buffer(sizeToRead); that._logger.log(that.DEBUG, LOG_ID + "(uploadAFileByChunk) sizeToRead=", sizeToRead, ", minRange : ", minRange, ", max : ", max, ", buff.byteLength : ", buf.byteLength); let promiseDeferred = new Deferred(); //promiseArray.push(promiseDeferred.promise); promiseQueue.add(() => { fs.readSync(fd, buf, 0, sizeToRead, null); partialSent(promiseDeferred, buf, i); return promiseDeferred.promise; }); } /* let promisesCompletion = () => { this._rest.sendPartialFileCompletion(fileDescriptor.id) .then( (response) => { that._logger.log(that.INFO, LOG_ID + "(uploadAFileByChunk) success"); fileDescriptor.state = "uploaded"; fileDescriptor.chunkPerformed = 0; fileDescriptor.chunkTotalNumber = 0; fileDescriptor.chunkPerformedPercent = 0; // progressCallback(fileDescriptor); deferred.resolve(fileDescriptor); }) .catch((errorResponse) => { deferred.reject(errorResponse); }); }; let promisesReject = (errorResponse) => { deferred.reject(errorResponse); }; that.transferPromiseQueue.addPromiseArray(promiseArray, promisesCompletion, promisesReject); // */ promiseQueue.add(() => { return this._rest.sendPartialFileCompletion(fileDescriptor.id) .then( (response) => { that._logger.log(that.INFO, LOG_ID + "(uploadAFileByChunk) success"); fileDescriptor.state = "uploaded"; fileDescriptor.chunkPerformed = 0; fileDescriptor.chunkTotalNumber = 0; fileDescriptor.chunkPerformedPercent = 0; // progressCallback(fileDescriptor); deferred.resolve(fileDescriptor); }) .catch((errorResponse) => { return deferred.reject(errorResponse); }); }); // */ return deferred.promise; } // Fallback if capabilities retrieval fails or file is small enough to upload the whole file in one go // progressCallback(fileDescriptor); return that._uploadAFile(fileDescriptor.id, filePath, fileDescriptor.typeMIME) .then( (response) => { that._logger.log(that.INFO, LOG_ID + "(uploadAFileByChunk) uploadAFile success"); // progressCallback(fileDescriptor); return Promise.resolve(fileDescriptor); }); } isTransferInProgress() { return this.transferPromiseQueue.isTransferInProgress(); } cancelAllTransfers() { this.transferPromiseQueue.cancelAllTransfers(); } /** * @method getBlobFromUrlWithOptimization * @description * Method creates blob from a file retrieved from server using optimization (range request) whenever necessary * * @param {string} url (required) server url for request * @param {string} mime (required) Mime type of the blob to be created * @param {number} fileSize=0 (optional) size of file to be retrieved. Default: 0 * @param {string} fileName (optional) name of file to be downloaded * @param {string} uploadedDate * @returns {Promise<{ * buffer : Array, * type: string, // mime type * fileSize: number, * fileName: string * }>} Object created from data received from server. */ async getBlobFromUrlWithOptimization(url : string, mime : string, fileSize : number=0, fileName : string, uploadedDate:string ) { let that = this; if (fileSize==null) { fileSize = 0; } if (fileName==null) { fileName = ""; } if (uploadedDate==null) { uploadedDate = ""; } if (uploadedDate.length !== 0) { // NEED TO BE CORREDTED TO BE USED IN NODE RAINBOW SDK url += "?update=" + MD5.hexdigest(uploadedDate); } if (!! (await that.capabilities).maxChunkSizeDownload && fileSize !== 0 && fileSize > (await that.capabilities).maxChunkSizeDownload) { return new Promise(async(resolve, reject) => { let range = (await that.capabilities).maxChunkSizeDownload; if (range > (ONE_MEGABYTE * 10)) { range = (ONE_MEGABYTE * 10); } let minRange = 0; let maxRange = range - 1; let repetition = Math.ceil(fileSize / range); let numberOfChunks = Math.ceil(fileSize / range); let blobArray = new Array(repetition); that._logger.log(that.INTERNAL, LOG_ID + "(getBlobFromUrlWithOptimization) - range : ", range, ", fileSize : ", fileSize, ", repetition : ", repetition, ", ONE_MEGABYTE : ", ONE_MEGABYTE, ", numberOfChunks : ", numberOfChunks); that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) : " + repetition + " chunks to be downloaded"); let promiseArray = []; for (let i = 0; repetition > 0; i++ , repetition-- , minRange += range, maxRange += range) { that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) - get partial buffer, iter : ", i, ", minRange : ", minRange, ", maxRange : ", maxRange); promiseArray.push( //let result = await that.getPartialDataFromServer(url, minRange, maxRange, i) //let result = await that.getPartialBufferFromServer(url, minRange, maxRange, i) //let result = await that.getPartialBufferFromServer(url, minRange, maxRange, i) that.getPartialBufferFromServer(url, minRange, maxRange, i) .then((response) => { let index = response['index']; that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) - getPartialBufferFromServer iter ", i, "/", numberOfChunks, " succeed! Store at index : ", index); blobArray[index] = response['data']; //return (response['data']); return ( { "code":0, "label" : "OK"} ); }).catch((error) => { that._logger.log(that.ERROR, LOG_ID + "(getBlobFromUrlWithOptimization) - Error getPartialBufferFromServer iter : ", i, "/", numberOfChunks, " error : ", error); }) ); //that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) - getPartialBufferFromServer iter : ", i, "/", numberOfChunks,", result : ", result); // repetition =0; await pause(20); } that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) - wait for the ", numberOfChunks, " chunks to be downloaded!"); //promiseArray.push(Promise.resolve()); Promise.all(promiseArray) .then( () => { /* NEED TO BE CORREDTED TO BE USED IN NODE RAINBOW SDK let blob = new Blob(blobArray, { type: mime }); that._logger.log(that.INFO, LOG_ID + "getBlobFromUrlWithOptimization success"); resolve(blob); */ that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) - all the ", numberOfChunks, " chunks downloaded!"); let blob = { buffer : blobArray, type: mime, fileSize: fileSize, fileName: fileName }; // */ resolve(blob); }, (errorResponse) => { let errorMessage = "(getBlobFromUrlWithOptimization) failure : " + errorResponse.message; that._logger.log(that.ERROR, LOG_ID + "(getBlobFromUrlWithOptimization) Error."); that._logger.log(that.INTERNALERROR, LOG_ID + "(getBlobFromUrlWithOptimization) : ", errorResponse); return reject(ErrorManager.getErrorManager().OTHERERROR(errorMessage, errorMessage)); /* let error = this.errorHelperService.handleError(errorResponse); let errorDataObj = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(errorResponse.data))); let translatedErrorMessage = that.errorHelperService.getLocalizedError(errorDataObj.errorDetailsCode); that._logger.log(that.INFO, LOG_ID + "" + translatedErrorMessage ? translatedErrorMessage : error.message); */ //reject(errorMessage); } ); }); } else { return this.getBlobFromUrl(url, mime, fileSize, fileName); } }; /** * @method getBlobFromUrlWithOptimizationObserver * @description * Method creates blob from a file retrieved from server using optimization (range request) whenever necessary * * @param {string} url (required) server url for request * @param {string} mime (required) Mime type of the blob to be created * @param {number} fileSize=0 (optional) size of file to be retrieved. Default: 0 * @param {string} fileName (optional) name of file to be downloaded * @param {string} uploadedDate * @returns {Promise} Observer returning a Blob created from data received from server * */ async getBlobFromUrlWithOptimizationObserver(url: string, mime: string, fileSize: number=0, fileName: string, uploadedDate: string ) : Promise> { let that = this; if (fileSize==null) { fileSize = 0; } if (fileName==null) { fileName = ""; } if (uploadedDate==null) { uploadedDate = ""; } if (uploadedDate.length !== 0) { // NEED TO BE CORREDTED TO BE USED IN NODE RAINBOW SDK url += "?update=" + MD5.hexdigest(uploadedDate); } let maxChunkSizeDownload = (await that.capabilities).maxChunkSizeDownload ; // / 80 to get alf of 1 Mo when server get us a 10Mo maxChunckSizeDownload; that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) - maxChunkSizeDownload : " + maxChunkSizeDownload); // process.exit(-1); if (!! maxChunkSizeDownload && fileSize !== 0 && fileSize > maxChunkSizeDownload) { let promiseArray = []; let obsrv$ : Observable = Observable.create(async (subject : Subscriber) => { let chunckLoaded = 0; let range = maxChunkSizeDownload; if (range > (ONE_MEGABYTE * 10)) { that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) : set range to 10 Mo."); range = (ONE_MEGABYTE * 10) ; } // let minRange = 0; let maxRange = range - 1; let repetition = Math.ceil(fileSize / range); let numberOfChunks = Math.ceil(fileSize / range); let blobArray = new Array(repetition); that._logger.log(that.INTERNAL, LOG_ID + "(getBlobFromUrlWithOptimization) - range : ", range, ", fileSize : ", fileSize, ", repetition : ", repetition, ", ONE_MEGABYTE : ", ONE_MEGABYTE, ", numberOfChunks : ", numberOfChunks); that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) : " + repetition + " chunks to be downloaded"); for (let i = 0; repetition > 0; i++ , repetition-- , minRange += range, maxRange += range) { that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) - get partial buffer, iter : ", i, ", minRange : ", minRange, ", maxRange : ", maxRange); promiseArray.push( new Promise ((resolve, reject)=> { that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) - push promise in Array iter : ", i, "/", numberOfChunks - 1 /* , ", result : ", result */ ); //let result = await that.getPartialDataFromServer(url, minRange, maxRange, i) //let result = /* // Start Test with out real download. chunckLoaded++; let index = 0; that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) - getPartialBufferFromServer Success iter ", i, "/", numberOfChunks - 1, " succeed! Store at index : ", index); //blobArray[index] = response['data']; subject.next(chunckLoaded * 100 / (numberOfChunks - 1 ) ); // Raise the percentage of loaded chunck. //return (response['data']); resolve ({"code": 0, "label": "OK"}); // End Test // */ that.getPartialBufferFromServer(url, minRange, maxRange, i).then((response) => { chunckLoaded++; let index = response['index']; that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) - getPartialBufferFromServer Success iter ", i, "/", numberOfChunks, " succeed! Store at index : ", index); blobArray[index] = response['data']; subject.next(chunckLoaded * 100 / numberOfChunks); // Raise the percentage of loaded chunck. //return (response['data']); resolve ({"code": 0, "label": "OK"}); }).catch((error) => { that._logger.log(that.ERROR, LOG_ID + "(getBlobFromUrlWithOptimization) - Error getPartialBufferFromServer iter : ", i, "/", numberOfChunks, " error : ", error); reject({"code":-1, "label": "Error while retrieving the chunck " + i + "/" + numberOfChunks}) }) // */ }) ); // repetition =0; await pause(20); } that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) - wait for the ", numberOfChunks, " chunks to be downloaded!"); //promiseArray.push(Promise.resolve()); Promise.all(promiseArray).then( () => { that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrlWithOptimization) - all the ", numberOfChunks, " chunks downloaded!"); let blob = { buffer : blobArray, type: mime, fileSize: fileSize, fileName: fileName }; // */ subject.next(blob); subject.complete();//blob }, (errorResponse) => { let errorMessage = "(getBlobFromUrlWithOptimization) failure : " + errorResponse.message; that._logger.log(that.ERROR, LOG_ID + "(getBlobFromUrlWithOptimization) Error."); that._logger.log(that.INTERNALERROR, LOG_ID + "(getBlobFromUrlWithOptimization) : ", errorResponse); subject.error(ErrorManager.getErrorManager().OTHERERROR(errorMessage, errorMessage)); } ); }); return Promise.resolve(obsrv$); } else { let obsrv$ : Observable = Observable.create(async (subject : Subscriber) => { subject.next(1); that.getBlobFromUrl(url, mime, fileSize, fileName).then((blob) => { subject.next(100); subject.next(blob); subject.complete();//blob }).catch((errorResponse)=>{ let errorMessage = "(getBlobFromUrlWithOptimization) failure : " + errorResponse.message; that._logger.log(that.ERROR, LOG_ID + "(getBlobFromUrlWithOptimization) Error."); that._logger.log(that.INTERNALERROR, LOG_ID + "(getBlobFromUrlWithOptimization) : ", errorResponse); subject.error(ErrorManager.getErrorManager().OTHERERROR(errorMessage, errorMessage)); }); }); return Promise.resolve(obsrv$); //return this.getBlobFromUrl(url, mime, fileSize, fileName); } }; /** * @description * Method creates blob from a file retrieved from server * * @private * @param {string} url (required) server url for request * @param {string} mime (required) Mime type of the blob to be created * @param {number} fileSize (required) size of file to be retrieved * @param {string} fileName (required) name of file to be downloaded * @returns {Promise<{ * buffer : Array, * type: string, // mime type * fileSize: number, * fileName: string * }>} Blob created from data received from server */ getBlobFromUrl(url: string, mime: string, fileSize: number, fileName: string) { let that = this; that._logger.log(that.INFO, LOG_ID + "(getBlobFromUrl)" ); that._logger.log(that.INTERNAL, LOG_ID + "(getBlobFromUrl) : " + url); return new Promise((resolve, reject) => { /*this.$http({ method: "GET", url: url, headers: this.authService.getRequestHeader(), responseType: 'arraybuffer' }) // */ that._rest.getBlobFromUrl(url).then( (response) => { // : ng.IHttpPromiseCallbackArg /* let blob = blobUtil.createBlob([response.data], { type: mime }); // */ let blobArray = new Array(0); blobArray.push(response); let blob = { buffer : blobArray, type: mime, fileSize: fileSize, fileName: fileName }; // */ /*let blob = new Blob([response.data], { type: mime }); // */ that._logger.log(that.DEBUG, LOG_ID + "(getBlobFromUrl) success"); resolve(blob); }, (errorResponse) => { let errorMessage = "(getBlobFromUrl) failure : " + errorResponse; that._logger.log(that.ERROR, LOG_ID + "(getBlobFromUrl) Error." ); that._logger.log(that.INTERNALERROR, LOG_ID + "(getBlobFromUrl) : ", errorResponse); let err = ErrorManager.getErrorManager().ERROR; err.msg = errorMessage; return reject(err); /* let error = this.errorHelperService.handleError(errorResponse); let errorDataObj = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(errorResponse.data))); let translatedErrorMessage = this.errorHelperService.getLocalizedError(errorDataObj.errorDetailsCode); this.$log.error("" + (translatedErrorMessage) ? translatedErrorMessage : error.message); // */ }); }); } /** * @description * Method retrieves user quota (capabilities) for user * * @returns {Object} user quota for user * */ getServerCapabilities() { return this._rest.getServerCapabilities(); } } module.exports.FileServerService = FileServer; export {FileServer as FileServerService};