You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					215 lines
				
				6.7 KiB
			
		
		
			
		
	
	
					215 lines
				
				6.7 KiB
			| 
											2 years ago
										 | "use strict"; | ||
|  | 
 | ||
|  | var utils = require("../utils"); | ||
|  | var ConvertWorker = require("./ConvertWorker"); | ||
|  | var GenericWorker = require("./GenericWorker"); | ||
|  | var base64 = require("../base64"); | ||
|  | var support = require("../support"); | ||
|  | var external = require("../external"); | ||
|  | 
 | ||
|  | var NodejsStreamOutputAdapter = null; | ||
|  | if (support.nodestream) { | ||
|  |     try { | ||
|  |         NodejsStreamOutputAdapter = require("../nodejs/NodejsStreamOutputAdapter"); | ||
|  |     } catch(e) { | ||
|  |         // ignore
 | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Apply the final transformation of the data. If the user wants a Blob for | ||
|  |  * example, it's easier to work with an U8intArray and finally do the | ||
|  |  * ArrayBuffer/Blob conversion. | ||
|  |  * @param {String} type the name of the final type | ||
|  |  * @param {String|Uint8Array|Buffer} content the content to transform | ||
|  |  * @param {String} mimeType the mime type of the content, if applicable. | ||
|  |  * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the content in the right format. | ||
|  |  */ | ||
|  | function transformZipOutput(type, content, mimeType) { | ||
|  |     switch(type) { | ||
|  |     case "blob" : | ||
|  |         return utils.newBlob(utils.transformTo("arraybuffer", content), mimeType); | ||
|  |     case "base64" : | ||
|  |         return base64.encode(content); | ||
|  |     default : | ||
|  |         return utils.transformTo(type, content); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Concatenate an array of data of the given type. | ||
|  |  * @param {String} type the type of the data in the given array. | ||
|  |  * @param {Array} dataArray the array containing the data chunks to concatenate | ||
|  |  * @return {String|Uint8Array|Buffer} the concatenated data | ||
|  |  * @throws Error if the asked type is unsupported | ||
|  |  */ | ||
|  | function concat (type, dataArray) { | ||
|  |     var i, index = 0, res = null, totalLength = 0; | ||
|  |     for(i = 0; i < dataArray.length; i++) { | ||
|  |         totalLength += dataArray[i].length; | ||
|  |     } | ||
|  |     switch(type) { | ||
|  |     case "string": | ||
|  |         return dataArray.join(""); | ||
|  |     case "array": | ||
|  |         return Array.prototype.concat.apply([], dataArray); | ||
|  |     case "uint8array": | ||
|  |         res = new Uint8Array(totalLength); | ||
|  |         for(i = 0; i < dataArray.length; i++) { | ||
|  |             res.set(dataArray[i], index); | ||
|  |             index += dataArray[i].length; | ||
|  |         } | ||
|  |         return res; | ||
|  |     case "nodebuffer": | ||
|  |         return Buffer.concat(dataArray); | ||
|  |     default: | ||
|  |         throw new Error("concat : unsupported type '"  + type + "'"); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Listen a StreamHelper, accumulate its content and concatenate it into a | ||
|  |  * complete block. | ||
|  |  * @param {StreamHelper} helper the helper to use. | ||
|  |  * @param {Function} updateCallback a callback called on each update. Called | ||
|  |  * with one arg : | ||
|  |  * - the metadata linked to the update received. | ||
|  |  * @return Promise the promise for the accumulation. | ||
|  |  */ | ||
|  | function accumulate(helper, updateCallback) { | ||
|  |     return new external.Promise(function (resolve, reject){ | ||
|  |         var dataArray = []; | ||
|  |         var chunkType = helper._internalType, | ||
|  |             resultType = helper._outputType, | ||
|  |             mimeType = helper._mimeType; | ||
|  |         helper | ||
|  |             .on("data", function (data, meta) { | ||
|  |                 dataArray.push(data); | ||
|  |                 if(updateCallback) { | ||
|  |                     updateCallback(meta); | ||
|  |                 } | ||
|  |             }) | ||
|  |             .on("error", function(err) { | ||
|  |                 dataArray = []; | ||
|  |                 reject(err); | ||
|  |             }) | ||
|  |             .on("end", function (){ | ||
|  |                 try { | ||
|  |                     var result = transformZipOutput(resultType, concat(chunkType, dataArray), mimeType); | ||
|  |                     resolve(result); | ||
|  |                 } catch (e) { | ||
|  |                     reject(e); | ||
|  |                 } | ||
|  |                 dataArray = []; | ||
|  |             }) | ||
|  |             .resume(); | ||
|  |     }); | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * An helper to easily use workers outside of JSZip. | ||
|  |  * @constructor | ||
|  |  * @param {Worker} worker the worker to wrap | ||
|  |  * @param {String} outputType the type of data expected by the use | ||
|  |  * @param {String} mimeType the mime type of the content, if applicable. | ||
|  |  */ | ||
|  | function StreamHelper(worker, outputType, mimeType) { | ||
|  |     var internalType = outputType; | ||
|  |     switch(outputType) { | ||
|  |     case "blob": | ||
|  |     case "arraybuffer": | ||
|  |         internalType = "uint8array"; | ||
|  |         break; | ||
|  |     case "base64": | ||
|  |         internalType = "string"; | ||
|  |         break; | ||
|  |     } | ||
|  | 
 | ||
|  |     try { | ||
|  |         // the type used internally
 | ||
|  |         this._internalType = internalType; | ||
|  |         // the type used to output results
 | ||
|  |         this._outputType = outputType; | ||
|  |         // the mime type
 | ||
|  |         this._mimeType = mimeType; | ||
|  |         utils.checkSupport(internalType); | ||
|  |         this._worker = worker.pipe(new ConvertWorker(internalType)); | ||
|  |         // the last workers can be rewired without issues but we need to
 | ||
|  |         // prevent any updates on previous workers.
 | ||
|  |         worker.lock(); | ||
|  |     } catch(e) { | ||
|  |         this._worker = new GenericWorker("error"); | ||
|  |         this._worker.error(e); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | StreamHelper.prototype = { | ||
|  |     /** | ||
|  |      * Listen a StreamHelper, accumulate its content and concatenate it into a | ||
|  |      * complete block. | ||
|  |      * @param {Function} updateCb the update callback. | ||
|  |      * @return Promise the promise for the accumulation. | ||
|  |      */ | ||
|  |     accumulate : function (updateCb) { | ||
|  |         return accumulate(this, updateCb); | ||
|  |     }, | ||
|  |     /** | ||
|  |      * Add a listener on an event triggered on a stream. | ||
|  |      * @param {String} evt the name of the event | ||
|  |      * @param {Function} fn the listener | ||
|  |      * @return {StreamHelper} the current helper. | ||
|  |      */ | ||
|  |     on : function (evt, fn) { | ||
|  |         var self = this; | ||
|  | 
 | ||
|  |         if(evt === "data") { | ||
|  |             this._worker.on(evt, function (chunk) { | ||
|  |                 fn.call(self, chunk.data, chunk.meta); | ||
|  |             }); | ||
|  |         } else { | ||
|  |             this._worker.on(evt, function () { | ||
|  |                 utils.delay(fn, arguments, self); | ||
|  |             }); | ||
|  |         } | ||
|  |         return this; | ||
|  |     }, | ||
|  |     /** | ||
|  |      * Resume the flow of chunks. | ||
|  |      * @return {StreamHelper} the current helper. | ||
|  |      */ | ||
|  |     resume : function () { | ||
|  |         utils.delay(this._worker.resume, [], this._worker); | ||
|  |         return this; | ||
|  |     }, | ||
|  |     /** | ||
|  |      * Pause the flow of chunks. | ||
|  |      * @return {StreamHelper} the current helper. | ||
|  |      */ | ||
|  |     pause : function () { | ||
|  |         this._worker.pause(); | ||
|  |         return this; | ||
|  |     }, | ||
|  |     /** | ||
|  |      * Return a nodejs stream for this helper. | ||
|  |      * @param {Function} updateCb the update callback. | ||
|  |      * @return {NodejsStreamOutputAdapter} the nodejs stream. | ||
|  |      */ | ||
|  |     toNodejsStream : function (updateCb) { | ||
|  |         utils.checkSupport("nodestream"); | ||
|  |         if (this._outputType !== "nodebuffer") { | ||
|  |             // an object stream containing blob/arraybuffer/uint8array/string
 | ||
|  |             // is strange and I don't know if it would be useful.
 | ||
|  |             // I you find this comment and have a good usecase, please open a
 | ||
|  |             // bug report !
 | ||
|  |             throw new Error(this._outputType + " is not supported by this method"); | ||
|  |         } | ||
|  | 
 | ||
|  |         return new NodejsStreamOutputAdapter(this, { | ||
|  |             objectMode : this._outputType !== "nodebuffer" | ||
|  |         }, updateCb); | ||
|  |     } | ||
|  | }; | ||
|  | 
 | ||
|  | 
 | ||
|  | module.exports = StreamHelper; |