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.
141 lines
4.0 KiB
141 lines
4.0 KiB
/*global Blob,File*/ |
|
|
|
/** |
|
* Module requirements |
|
*/ |
|
|
|
var isArray = require('isarray'); |
|
var isBuf = require('./is-buffer'); |
|
var toString = Object.prototype.toString; |
|
var withNativeBlob = typeof global.Blob === 'function' || toString.call(global.Blob) === '[object BlobConstructor]'; |
|
var withNativeFile = typeof global.File === 'function' || toString.call(global.File) === '[object FileConstructor]'; |
|
|
|
/** |
|
* Replaces every Buffer | ArrayBuffer in packet with a numbered placeholder. |
|
* Anything with blobs or files should be fed through removeBlobs before coming |
|
* here. |
|
* |
|
* @param {Object} packet - socket.io event packet |
|
* @return {Object} with deconstructed packet and list of buffers |
|
* @api public |
|
*/ |
|
|
|
exports.deconstructPacket = function(packet) { |
|
var buffers = []; |
|
var packetData = packet.data; |
|
var pack = packet; |
|
pack.data = _deconstructPacket(packetData, buffers); |
|
pack.attachments = buffers.length; // number of binary 'attachments' |
|
return {packet: pack, buffers: buffers}; |
|
}; |
|
|
|
function _deconstructPacket(data, buffers) { |
|
if (!data) return data; |
|
|
|
if (isBuf(data)) { |
|
var placeholder = { _placeholder: true, num: buffers.length }; |
|
buffers.push(data); |
|
return placeholder; |
|
} else if (isArray(data)) { |
|
var newData = new Array(data.length); |
|
for (var i = 0; i < data.length; i++) { |
|
newData[i] = _deconstructPacket(data[i], buffers); |
|
} |
|
return newData; |
|
} else if (typeof data === 'object' && !(data instanceof Date)) { |
|
var newData = {}; |
|
for (var key in data) { |
|
newData[key] = _deconstructPacket(data[key], buffers); |
|
} |
|
return newData; |
|
} |
|
return data; |
|
} |
|
|
|
/** |
|
* Reconstructs a binary packet from its placeholder packet and buffers |
|
* |
|
* @param {Object} packet - event packet with placeholders |
|
* @param {Array} buffers - binary buffers to put in placeholder positions |
|
* @return {Object} reconstructed packet |
|
* @api public |
|
*/ |
|
|
|
exports.reconstructPacket = function(packet, buffers) { |
|
packet.data = _reconstructPacket(packet.data, buffers); |
|
packet.attachments = undefined; // no longer useful |
|
return packet; |
|
}; |
|
|
|
function _reconstructPacket(data, buffers) { |
|
if (!data) return data; |
|
|
|
if (data && data._placeholder) { |
|
return buffers[data.num]; // appropriate buffer (should be natural order anyway) |
|
} else if (isArray(data)) { |
|
for (var i = 0; i < data.length; i++) { |
|
data[i] = _reconstructPacket(data[i], buffers); |
|
} |
|
} else if (typeof data === 'object') { |
|
for (var key in data) { |
|
data[key] = _reconstructPacket(data[key], buffers); |
|
} |
|
} |
|
|
|
return data; |
|
} |
|
|
|
/** |
|
* Asynchronously removes Blobs or Files from data via |
|
* FileReader's readAsArrayBuffer method. Used before encoding |
|
* data as msgpack. Calls callback with the blobless data. |
|
* |
|
* @param {Object} data |
|
* @param {Function} callback |
|
* @api private |
|
*/ |
|
|
|
exports.removeBlobs = function(data, callback) { |
|
function _removeBlobs(obj, curKey, containingObject) { |
|
if (!obj) return obj; |
|
|
|
// convert any blob |
|
if ((withNativeBlob && obj instanceof Blob) || |
|
(withNativeFile && obj instanceof File)) { |
|
pendingBlobs++; |
|
|
|
// async filereader |
|
var fileReader = new FileReader(); |
|
fileReader.onload = function() { // this.result == arraybuffer |
|
if (containingObject) { |
|
containingObject[curKey] = this.result; |
|
} |
|
else { |
|
bloblessData = this.result; |
|
} |
|
|
|
// if nothing pending its callback time |
|
if(! --pendingBlobs) { |
|
callback(bloblessData); |
|
} |
|
}; |
|
|
|
fileReader.readAsArrayBuffer(obj); // blob -> arraybuffer |
|
} else if (isArray(obj)) { // handle array |
|
for (var i = 0; i < obj.length; i++) { |
|
_removeBlobs(obj[i], i, obj); |
|
} |
|
} else if (typeof obj === 'object' && !isBuf(obj)) { // and object |
|
for (var key in obj) { |
|
_removeBlobs(obj[key], key, obj); |
|
} |
|
} |
|
} |
|
|
|
var pendingBlobs = 0; |
|
var bloblessData = data; |
|
_removeBlobs(bloblessData); |
|
if (!pendingBlobs) { |
|
callback(bloblessData); |
|
} |
|
};
|
|
|