142 lines
4.0 KiB
JavaScript
142 lines
4.0 KiB
JavaScript
/*global Blob,File*/
|
|
|
|
/**
|
|
* Module requirements
|
|
*/
|
|
|
|
var isArray = require('isarray');
|
|
var isBuf = require('./is-buffer');
|
|
var toString = Object.prototype.toString;
|
|
var withNativeBlob = typeof Blob === 'function' || (typeof Blob !== 'undefined' && toString.call(Blob) === '[object BlobConstructor]');
|
|
var withNativeFile = typeof File === 'function' || (typeof File !== 'undefined' && toString.call(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);
|
|
}
|
|
};
|