Le problème avec UTF-8 est que ce n'est pas le codage le plus efficace en termes d'espace. De plus, certaines séquences aléatoires d'octets binaires sont des encodages UTF-8 invalides. Donc vous ne pouvez pas simplement interpréter une séquence d'octets binaires aléatoire comme des données UTF-8 car ce serait un encodage UTF-8 invalide. L'avantage de cette contrainte sur l'encodage UTF-8 est que cela le rend robuste et permet de localiser le début et la fin des caractères multioctets peu importe l'octet à partir duquel nous commençons à regarder.
En conséquence, si l'encodage d'une valeur d'octet dans la plage [0..127] nécessiterait seulement un octet dans l'encodage UTF-8, l'encodage d'une valeur d'octet dans la plage [128..255] nécessiterait 2 octets ! Pire que cela. En JSON, les caractères de contrôle, " et \ ne sont pas autorisés à apparaître dans une chaîne. Donc les données binaires nécessiteraient une certaine transformation pour être correctement encodées.
Regardons. Si nous supposons des valeurs d'octets aléatoires uniformément distribuées dans nos données binaires, alors, en moyenne, la moitié des octets seraient encodés en un octet et l'autre moitié en deux octets. Les données binaires encodées en UTF-8 auraient une taille 150% de la taille initiale.
L'encodage Base64 ne dépasse que de 133% la taille initiale. Donc l'encodage Base64 est plus efficace.
Que diriez-vous d'utiliser un autre encodage de base ? En UTF-8, l'encodage des 128 valeurs ASCII est le plus efficace en termes d'espace. En 8 bits, vous pouvez stocker 7 bits. Donc si nous coupons les données binaires en morceaux de 7 bits pour les stocker dans chaque octet d'une chaîne encodée en UTF-8, les données encodées ne grossiraient que jusqu'à 114% de la taille initiale. Mieux que Base64. Malheureusement, nous ne pouvons pas utiliser cette astuce simple car JSON n'autorise pas certains caractères ASCII. Les 33 caractères de contrôle de l'ASCII ( [0..31] et 127) ainsi que " et \ doivent être exclus. Cela ne nous laisse que 128-35 = 93 caractères.
Donc en théorie, nous pourrions définir un encodage Base93 qui ferait grossir la taille encodée à 8/log2(93) = 8*log10(2)/log10(93) = 122%. Mais un encodage Base93 ne serait pas aussi pratique qu'un encodage Base64. Base64 nécessite de découper la séquence d'octets d'entrée en morceaux de 6 bits pour lesquels des opérations simples en bits fonctionnent bien. De plus, 133% n'est pas beaucoup plus que 122%.
C'est pourquoi je suis arrivé indépendamment à la conclusion commune que Base64 est effectivement le meilleur choix pour encoder des données binaires en JSON. Ma réponse présente une justification à ce sujet. Je conviens que ce n'est pas très attrayant d'un point de vue performances, mais pensez aussi au bénéfice d'utiliser JSON avec sa représentation de chaîne lisible par l'homme, facile à manipuler dans tous les langages de programmation.
Si les performances sont critiques, alors un encodage binaire pur devrait être envisagé comme remplacement de JSON. Mais avec JSON, ma conclusion est que Base64 est le meilleur choix.
50 votes
Pour le téléchargement : vous pourriez être surpris de la façon dont le base64 se compresse sous gzip, donc si vous avez gzip activé sur votre serveur, vous êtes probablement également en sécurité.
6 votes
Une autre solution digne msgpack.org pour les nerds hardcore : github.com/msgpack/msgpack/blob/master/spec.md
5 votes
@cloudfeet, Une fois par utilisateur par action. Un très gros deal.
7 votes
Notez que les caractères sont généralement 2 octets de mémoire chacun. Ainsi, le base64 pourrait donner un surdébit de +33 % (4/3) sur le fil, mais mettre ces données sur le fil, les récupérer et les utiliser, nécessiterait un surdébit de +166 % (8/3) surdébit. En un mot : si une chaîne Javascript a une longueur maximale de 100k caractères, vous ne pouvez représenter que 37,5k octets de données en utilisant le base64, pas 75k octets de données. Ces chiffres peuvent être un obstacle dans de nombreuses parties de l'application, par exemple
JSON.parse
etc. ......0 votes
....... Comparez ces chiffres aux économies que vous pouvez réaliser si vous convertissez les données binaires brutes en points de code, puis convertissez ces points de code en UTF-8. Même une conversion simple en utilisant l'encodage pour les points de code
0x00
à0xff
reviendrait en moyenne à un surdébit de seulement +50%). et ................0 votes
................. la conversion en utilisant l'encodage pour les points de code
0x00
à0xffff
entraîne un surcoût d'environ ~48,5%. La conversion en utilisant l'encodage pour les points de code0x00
à0x10ffff
entraîne un surcoût d'environ 39,8%. Il s'agit de 71,5 ko de données que vous pouvez représenter avec 100 k caractères au lieu des 37,5 ko de base64.10 votes
@Pacerier "environ 2 octets de mémoire [par caractère]" n'est pas précis. Par exemple, v8 dispose de chaînes OneByte et TwoByte. Les chaînes TwoByte ne sont utilisées que lorsque nécessaire pour éviter une consommation grotesque de mémoire. Base64 est encodable avec des chaînes d'un octet.
0 votes
@cloudfeet il ne devrait pas être si surprenant que base64 compresse si bien, surtout si les données originales se compressent bien dès le départ.
0 votes
Avez-vous pensé à utiliser ubjson à la place? C'est un joli format et interopérable avec json. Vos données binaires seront encodées sous forme d'un tableau d'entiers lorsqu'elles sont converties en json. Il a également un type MIME standard et permettra d'économiser de l'espace sur vos autres données en prime.
0 votes
Les gens réfléchissent trop à cela. Tout est binaire quoi qu'il arrive. Le problème avec les données binaires dans les chaînes encodées en JSON est que certains octets peuvent correspondre à des caractères spéciaux réservés pour JSON. La solution consiste à vérifier chaque octet binaire comme s'il s'agissait d'un caractère et à l'échapper comme s'il s'agissait de caractères spéciaux, puis à déséchapper à l'autre bout et à traiter comme binaire.