减少从浏览器发送到服务器的对象数据的最佳方法

时间:2019-11-03 17:53:11

标签: javascript json compression

我们将以下数据从浏览器发送到服务器,除了JSON之外,对数据进行序列化/反序列化的最佳方法是什么?

我们最初将数据保存在客户端浏览器中,然后在常规检查点将其发送到服务器。由于数据的大小,发送数据时会占用大量浏览器内存以及网络。我们希望减少发送到服务器的数据的大小,因为每个对象的keys大多数都是相同的,但是values有所变化。

[
   {
      "range":{
         "sLineNumber":3,
         "sColumn":3,
         "eLineNumber":3,
         "eColumn":3
      },
      "rLength":0,
      "text":"\n",
      "rOffset":4,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":4,
         "sColumn":1,
         "eLineNumber":4,
         "eColumn":1
      },
      "rLength":0,
      "text":"\n",
      "rOffset":5,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":5,
         "sColumn":1,
         "eLineNumber":5,
         "eColumn":1
      },
      "rLength":0,
      "text":"\n",
      "rOffset":6,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":1,
         "eLineNumber":6,
         "eColumn":1
      },
      "rLength":0,
      "text":"f",
      "rOffset":7,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":2,
         "eLineNumber":6,
         "eColumn":2
      },
      "rLength":0,
      "text":"a",
      "rOffset":8,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":3,
         "eLineNumber":6,
         "eColumn":3
      },
      "rLength":0,
      "text":"s",
      "rOffset":9,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":4,
         "eLineNumber":6,
         "eColumn":4
      },
      "rLength":0,
      "text":"d",
      "rOffset":10,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":5,
         "eLineNumber":6,
         "eColumn":5
      },
      "rLength":0,
      "text":"f",
      "rOffset":11,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":6,
         "eLineNumber":6,
         "eColumn":6
      },
      "rLength":0,
      "text":"a",
      "rOffset":12,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":7,
         "eLineNumber":6,
         "eColumn":7
      },
      "rLength":0,
      "text":"s",
      "rOffset":13,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":8,
         "eLineNumber":6,
         "eColumn":8
      },
      "rLength":0,
      "text":"f",
      "rOffset":14,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":9,
         "eLineNumber":6,
         "eColumn":9
      },
      "rLength":0,
      "text":"s",
      "rOffset":15,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":10,
         "eLineNumber":6,
         "eColumn":10
      },
      "rLength":0,
      "text":"a",
      "rOffset":16,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":11,
         "eLineNumber":6,
         "eColumn":11
      },
      "rLength":0,
      "text":"f",
      "rOffset":17,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":12,
         "eLineNumber":6,
         "eColumn":12
      },
      "rLength":0,
      "text":"s",
      "rOffset":18,
      "rMoveMarkers":false
   }
]

我们认为的一种方法是将它们作为不带键的数组发送,因为我们知道每个key的位置。不确定是否有任何可用于此转换的软件包。

4 个答案:

答案 0 :(得分:4)

  

我们希望减少发送到服务器的数据的大小,因为每个对象的keys大部分都是相同的,但是values有所变化。

如果您将数据视为表格,则可以将每一列表示为对象属性,并在数组中包含行值,如下所示:

{
  "sLineNumber": [3, 4,         /* ... */ ],
  "sColumn":     [3, 1,         /* ... */ ],
  "eLineNumber": [3, 4,         /* ... */ ],
  "eColumn":     [3, 1,         /* ... */ ],
  "rLength":     [0, 0,         /* ... */ ],
  "text":        ["\n", "\n",   /* ... */ ],
  "rOffset":     [4, 5,         /* ... */ ],
  "rMoveMarkers":[false, false, /* ... */ ]
}

此结构保留所有属性名称("range"除外),并且内存效率更高。

数据仍然可以序列化为JSON,大小仅为原始结构的25%。

1中使用整数(0true)代替布尔值(false"rMoveMarkers",可以进一步减小大小。< / p>

快速且肮脏的代码可用于重组以下代码段中的数据:

var data = [
   {
      "range":{
         "sLineNumber":3,
         "sColumn":3,
         "eLineNumber":3,
         "eColumn":3
      },
      "rLength":0,
      "text":"\n",
      "rOffset":4,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":4,
         "sColumn":1,
         "eLineNumber":4,
         "eColumn":1
      },
      "rLength":0,
      "text":"\n",
      "rOffset":5,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":5,
         "sColumn":1,
         "eLineNumber":5,
         "eColumn":1
      },
      "rLength":0,
      "text":"\n",
      "rOffset":6,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":1,
         "eLineNumber":6,
         "eColumn":1
      },
      "rLength":0,
      "text":"f",
      "rOffset":7,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":2,
         "eLineNumber":6,
         "eColumn":2
      },
      "rLength":0,
      "text":"a",
      "rOffset":8,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":3,
         "eLineNumber":6,
         "eColumn":3
      },
      "rLength":0,
      "text":"s",
      "rOffset":9,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":4,
         "eLineNumber":6,
         "eColumn":4
      },
      "rLength":0,
      "text":"d",
      "rOffset":10,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":5,
         "eLineNumber":6,
         "eColumn":5
      },
      "rLength":0,
      "text":"f",
      "rOffset":11,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":6,
         "eLineNumber":6,
         "eColumn":6
      },
      "rLength":0,
      "text":"a",
      "rOffset":12,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":7,
         "eLineNumber":6,
         "eColumn":7
      },
      "rLength":0,
      "text":"s",
      "rOffset":13,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":8,
         "eLineNumber":6,
         "eColumn":8
      },
      "rLength":0,
      "text":"f",
      "rOffset":14,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":9,
         "eLineNumber":6,
         "eColumn":9
      },
      "rLength":0,
      "text":"s",
      "rOffset":15,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":10,
         "eLineNumber":6,
         "eColumn":10
      },
      "rLength":0,
      "text":"a",
      "rOffset":16,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":11,
         "eLineNumber":6,
         "eColumn":11
      },
      "rLength":0,
      "text":"f",
      "rOffset":17,
      "rMoveMarkers":false
   },
   {
      "range":{
         "sLineNumber":6,
         "sColumn":12,
         "eLineNumber":6,
         "eColumn":12
      },
      "rLength":0,
      "text":"s",
      "rOffset":18,
      "rMoveMarkers":false
   }
];

function transform(data){
  var transformed = {};
  ['rLength', 'text', 'rOffset', 'rMoveMarkers']
    .map(x => transformed[x] = data.map(y => y[x]));
  ['sLineNumber', 'sColumn', 'eLineNumber', 'eColumn']
    .map(x => transformed[x] = data.map(y => y.range[x]));
  return transformed;
}

var originalLength = JSON.stringify(data).length;
var transformedLength = JSON.stringify(transform(data)).length;

console.log(
  'Reduced to ' + 
  (100 * transformedLength / originalLength).toFixed(1) + 
  '% size of original, from ' + originalLength + ' characters to ' + 
  transformedLength + ' characters.'
);
console.log(transform(data));

  

我们认为的一种方法是将它们作为不带键的数组发送,因为我们知道每个key的位置。不确定是否有任何可用于此转换的软件包。

可以将数据简化为二维数组:

function transform(data) {
  return ['rLength', 'text', 'rOffset', 'rMoveMarkers']
    .map(x => data.map(y => y[x]))
    .concat(
      ['sLineNumber', 'sColumn', 'eLineNumber', 'eColumn']
      .map(x => data.map(y => y.range[x]))
    );
}

请注意,与上面的数据结构相比,放下键(属性名称)仅会减少一点点,而这又使跟踪错误或解决任何问题变得更加困难,因为您必须确保不要无意间混淆哪个数组属于哪个键。

下面的代码段中的演示代码:

var data = [{
    "range": {
      "sLineNumber": 3,
      "sColumn": 3,
      "eLineNumber": 3,
      "eColumn": 3
    },
    "rLength": 0,
    "text": "\n",
    "rOffset": 4,
    "rMoveMarkers": false
  },
  {
    "range": {
      "sLineNumber": 4,
      "sColumn": 1,
      "eLineNumber": 4,
      "eColumn": 1
    },
    "rLength": 0,
    "text": "\n",
    "rOffset": 5,
    "rMoveMarkers": false
  },
  {
    "range": {
      "sLineNumber": 5,
      "sColumn": 1,
      "eLineNumber": 5,
      "eColumn": 1
    },
    "rLength": 0,
    "text": "\n",
    "rOffset": 6,
    "rMoveMarkers": false
  },
  {
    "range": {
      "sLineNumber": 6,
      "sColumn": 1,
      "eLineNumber": 6,
      "eColumn": 1
    },
    "rLength": 0,
    "text": "f",
    "rOffset": 7,
    "rMoveMarkers": false
  },
  {
    "range": {
      "sLineNumber": 6,
      "sColumn": 2,
      "eLineNumber": 6,
      "eColumn": 2
    },
    "rLength": 0,
    "text": "a",
    "rOffset": 8,
    "rMoveMarkers": false
  },
  {
    "range": {
      "sLineNumber": 6,
      "sColumn": 3,
      "eLineNumber": 6,
      "eColumn": 3
    },
    "rLength": 0,
    "text": "s",
    "rOffset": 9,
    "rMoveMarkers": false
  },
  {
    "range": {
      "sLineNumber": 6,
      "sColumn": 4,
      "eLineNumber": 6,
      "eColumn": 4
    },
    "rLength": 0,
    "text": "d",
    "rOffset": 10,
    "rMoveMarkers": false
  },
  {
    "range": {
      "sLineNumber": 6,
      "sColumn": 5,
      "eLineNumber": 6,
      "eColumn": 5
    },
    "rLength": 0,
    "text": "f",
    "rOffset": 11,
    "rMoveMarkers": false
  },
  {
    "range": {
      "sLineNumber": 6,
      "sColumn": 6,
      "eLineNumber": 6,
      "eColumn": 6
    },
    "rLength": 0,
    "text": "a",
    "rOffset": 12,
    "rMoveMarkers": false
  },
  {
    "range": {
      "sLineNumber": 6,
      "sColumn": 7,
      "eLineNumber": 6,
      "eColumn": 7
    },
    "rLength": 0,
    "text": "s",
    "rOffset": 13,
    "rMoveMarkers": false
  },
  {
    "range": {
      "sLineNumber": 6,
      "sColumn": 8,
      "eLineNumber": 6,
      "eColumn": 8
    },
    "rLength": 0,
    "text": "f",
    "rOffset": 14,
    "rMoveMarkers": false
  },
  {
    "range": {
      "sLineNumber": 6,
      "sColumn": 9,
      "eLineNumber": 6,
      "eColumn": 9
    },
    "rLength": 0,
    "text": "s",
    "rOffset": 15,
    "rMoveMarkers": false
  },
  {
    "range": {
      "sLineNumber": 6,
      "sColumn": 10,
      "eLineNumber": 6,
      "eColumn": 10
    },
    "rLength": 0,
    "text": "a",
    "rOffset": 16,
    "rMoveMarkers": false
  },
  {
    "range": {
      "sLineNumber": 6,
      "sColumn": 11,
      "eLineNumber": 6,
      "eColumn": 11
    },
    "rLength": 0,
    "text": "f",
    "rOffset": 17,
    "rMoveMarkers": false
  },
  {
    "range": {
      "sLineNumber": 6,
      "sColumn": 12,
      "eLineNumber": 6,
      "eColumn": 12
    },
    "rLength": 0,
    "text": "s",
    "rOffset": 18,
    "rMoveMarkers": false
  }
];

function transform(data) {
  return ['rLength', 'text', 'rOffset', 'rMoveMarkers']
    .map(x => data.map(y => y[x]))
    .concat(
      ['sLineNumber', 'sColumn', 'eLineNumber', 'eColumn']
      .map(x => data.map(y => y.range[x]))
    );
}

var originalLength = JSON.stringify(data).length;
var transformedLength = JSON.stringify(transform(data)).length;

console.log(
  'Reduced to ' +
  (100 * transformedLength / originalLength).toFixed(1) +
  '% size of original, from ' + originalLength + ' characters to ' +
  transformedLength + ' characters.'
);
console.log(transform(data));

答案 1 :(得分:3)

激活gzip压缩并将其作为不带键的数组发送。

答案 2 :(得分:1)

我建议创建到服务器的WebSocket连接,然后在数据创建后立即将其推送。如果您对JS进行了简化,那么您应该很容易就能每秒发送1,000个这些对象。

签出https://socket.io/,这是使用WebSockets的简便方法。

另一个限制因素将是您的浏览器代码。如果您使用React来确保停止它,从而使需求重新出现,那么您可能还需要优化它。

答案 3 :(得分:0)

以下几种方法可以实现最佳压缩效果:

  • 使用GZIP compression -压缩从服务器发送到客户端的所有数据。对于express.js的{​​{3}}。应该与其他实用程序一起使用。
  • 使用here is a library。正如作者所说的那样-它是语言无关,平台无关,可扩展的序列化结构化数据的机制-考虑使用XML,但它更小,更快,更简单。
  • 使用Google Protobuf-是JSON的高效二进制序列化格式。 “这就像JSON,但又快又小”。易于集成在任何平台上。

此外,如果您可以自己更新数据,则可以:

  • 如果键始终相同-删除键并仅保留值
  • 如果键可以不同-2个选项:
    • 具有2个属性的结构:键列表和带有值的列表列表
    {
       "keys": ["sLineNumber", "sColumn", "eLineNumber", ..., "rMoveMarkers"],
       "values": [
           [3, 4, ...],
           [3, 1, ...],
           [3, 4, ...],
           ...
           [false, false, ...],
       ]
    }
    
    • 具有键的键和具有所有值的值:
    {
        "sLineNumber": [3, 4, ...],
        "sColumn":     [3, 1, ...],
        "eLineNumber": [3, 4, ...],
        ...
        "rMoveMarkers":[false, false, ...]
    }
    
    • 甚至只是将列表列表作为响应。

与gZip一起使用-在大多数情况下应该足够了。