Données binaires dans la chaîne JSON. Quelque chose de mieux que la Base64

le format JSON nativement ne supporte pas les données binaires. Les données binaires doivent être échappées de sorte qu'elles puissent être placées dans un élément de chaîne (c'est-à-dire zéro ou plus de caractères Unicode dans les guillemets doubles en utilisant des escapes antislash) dans JSON.

une méthode évidente pour échapper aux données binaires est D'utiliser la Base64. Toutefois, la Base64 a des frais généraux de traitement élevés. De plus, il élargit 3 octets en 4 caractères, ce qui entraîne une augmentation de la taille des données d'environ 33%.

L'un des cas d'utilisation est le v0.8 draft of the CDMI cloud storage API specification . Vous créez des objets de données via un REST-Webservice en utilisant JSON, par exemple

PUT /MyContainer/BinaryObject HTTP/1.1
Host: cloud.example.com
Accept: application/vnd.org.snia.cdmi.dataobject+json
Content-Type: application/vnd.org.snia.cdmi.dataobject+json
X-CDMI-Specification-Version: 1.0
{
    "mimetype" : "application/octet-stream",
    "metadata" : [ ],
    "value" :   "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlz
    IHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2Yg
    dGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu
    dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRo
    ZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4=",
}

y a-t-il de meilleurs moyens et méthodes standard pour encoder des données binaires dans des chaînes JSON?

498
demandé sur Patrik 2009-09-18 12:08:05

15 réponses

il y a 94 caractères Unicode qui peuvent être représentés comme un octet selon la spécification JSON (si votre JSON est transmis en UTF-8). Avec cela à l'esprit, je pense que le mieux que vous pouvez faire l'espace-sage est base85 qui représente quatre octets en cinq caractères. Cependant, il ne s'agit que d'une amélioration de 7% par rapport à base64, c'est plus coûteux à calculer, et les implémentations sont moins courantes que pour base64, donc ce n'est probablement pas une victoire.

vous pouvez aussi il suffit de faire correspondre chaque octet d'entrée au caractère correspondant dans U+0000-U+00FF, puis de faire le codage minimum requis par la norme JSON pour passer ces caractères; l'avantage ici est que le décodage requis est nul au-delà des fonctions de construction, mais l'efficacité de l'espace est Mauvaise -- une expansion de 105% (si tous les octets d'entrée sont également probables) contre 25% pour base85 ou 33% pour base64.

verdict Final: base64 gagne, à mon avis, sur le motif que c'est commun, facile, et pas mauvais assez pour justifier un remplacement.

Voir aussi: base 91

382
répondu hobbs 2018-09-18 23:32:10

je sais que c'est une question vieille de près de 6 ans, mais je suis tombé sur le même problème, et j'ai pensé que je partagerais une solution: multipart/form-data.

en envoyant un formulaire multipart vous envoyez d'abord comme chaîne votre méta-données JSON , puis séparément envoyer comme binaire brut (image(s), wavs, etc) indexé par le Content-Disposition nom.

voici un joli tutoriel sur la façon de faire cela dans obj-c, et voici un article de blog qui explique comment partager les données de chaîne avec la limite de la forme, et le séparer des données binaires.

le seul changement que vous devez vraiment faire est du côté du serveur; vous devrez capturer vos méta-données qui devraient référencer les données binaires POST'ed de manière appropriée (en utilisant une limite contenu-Disposition).

accordé, il nécessite des travaillez du côté du serveur, mais si vous envoyez beaucoup d'images ou de grandes images, cela en vaut la peine. Combinez cela avec la compression gzip si vous voulez.

à mon humble avis l'envoi des données encodées en base64 est un hack; la RFC multipart/form-data a été créé pour des questions telles que: l'envoi de données binaires en combinaison avec du texte ou des méta-données.

192
répondu Ælex 2018-05-11 09:41:37

BSON (JSON binaire) peut fonctionner pour vous. http://en.wikipedia.org/wiki/BSON

Edit: POUR INFORMATION la bibliothèque json.net prend en charge la lecture et l'écriture de bson si vous êtes à la recherche d'un certain c# server côté amour.

30
répondu DarcyThomas 2012-09-24 03:49:06

le problème avec UTF-8 est qu'il n'est pas le codage le plus efficace de l'espace. En outre, certaines séquences binaires binaires aléatoires sont invalides UTF-8 encoding. Ainsi, vous ne pouvez pas simplement interpréter une séquence binaire binaire aléatoire comme des données UTF-8 parce qu'elle sera un encodage UTF-8 invalide. L'avantage de cette contrainte sur L'encodage UTF-8 est qu'elle le rend robuste et possible pour localiser les caractères multi octets au début et à la fin de n'importe quel octet que nous commençons à regarder.

en conséquence, si le codage d'une valeur d'un octet dans l'intervalle [0..127] ne nécessiterait qu'un seul octet dans L'encodage UTF-8, encodant une valeur de octet dans la gamme [128..255] nécessiterait 2 octets ! Pire que cela. Dans JSON, les caractères de contrôle, " et \ ne sont pas autorisés à apparaître dans une chaîne. Ainsi, les données binaires nécessiteraient une transformation pour être correctement encodées.

voyons. Si nous supposons aléatoire uniformément distribué valeurs d'octets dans nos données binaires, en moyenne, la moitié des octets est codé dans un octets et l'autre moitié sur deux octets. Les données binaires encodées UTF-8 auraient 150% de la taille initiale.

Base64 l'encodage ne croît que jusqu'à 133% de la taille initiale. L'encodage Base64 est donc plus efficace.

qu'en est-il de l'utilisation d'un autre encodage de Base ? En UTF-8, encoder les 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 UTF-8, le les données encodées augmenterait seulement à 114% de la taille initiale. Mieux que la Base64. Malheureusement, nous ne pouvons pas utiliser cette astuce facile parce que JSON ne permet pas certains ASCII chars. Les 33 caractères de contrôle D'ASCII ( [0..31] et 127) et les " et \ doivent être exclus. Cela nous laisse seulement 128-35 = 93 caractères.

ainsi, en théorie, nous pourrions définir un encodage Base93 qui augmenterait la taille encodée à 8/log2(93) = 8*log10(2)/log10(93) = 122%. Mais un encodage Base93 ne serait pas aussi pratique que un encodage Base64. Base64 nécessite de couper la séquence des octets d'entrée en morceaux de 6 bits pour lesquels une simple opération en bits fonctionne bien. À côté de 133% n'est pas beaucoup plus de 122%.

C'est pourquoi je suis venu indépendamment à la conclusion commune que Base64 est en effet le meilleur choix pour encoder des données binaires dans JSON. Ma réponse présente une justification. Je suis d'accord il n'est pas très intéressant du point de vue des performances, mais aussi envisager les avantages de l'utilisation de JSON avec homme lisible représentation de chaîne facile à manipuler dans tous les langages de programmation.

si la performance est critique qu'un encodage binaire pur devrait être considéré comme le remplacement de JSON. Mais avec JSON, ma conclusion est que Base64 est le meilleur.

25
répondu chmike 2013-09-22 19:35:11

si vous avez des problèmes de bande passante, essayez d'abord de compresser les données côté client, puis base64-it.

bel exemple d'une telle magie est à http://jszip.stuartk.co.uk / et plus de discussion à ce sujet est à application JavaScript de gzip

18
répondu andrej 2017-05-23 12:26:27

yEnc pourrait travailler pour vous:

http://en.wikipedia.org/wiki/Yenc

17
répondu richardtallent 2009-09-18 08:12:07

"151910920 de Sourire" format

il est très rapide à coder, Décoder et compact

comparaison de la vitesse (basée sur java mais significative néanmoins): https://github.com/eishay/jvm-serializers/wiki/

C'est aussi une extension de JSON qui vous permet de sauter l'encodage base64 pour les tableaux byte

les chaînes codées Smile peuvent être compressées lorsque l'espace est critique

9
répondu Stefano Fratini 2012-01-06 23:42:01

S'il est vrai que base64 a un taux d'expansion d'environ 33%, Il n'est pas nécessairement vrai que les frais généraux de traitement sont significativement plus élevés que cela: cela dépend vraiment de la bibliothèque/boîte à outils JSON que vous utilisez. L'encodage et le décodage sont des opérations simples et directes, et ils peuvent même être optimisés pour l'encodage des caractères wrt (comme JSON ne supporte que L'UTF-8/16/32) -- les caractères base64 sont toujours à un octet pour les entrées JSON String. Par exemple, sur la plate-forme Java, il existe des bibliothèques qui peuvent faire travail assez efficacement, de sorte que les frais généraux est principalement due à la taille élargie.

je suis d'accord avec les deux premières réponses:

  • base64 est simple, standard couramment utilisé, de sorte qu'il est peu probable de trouver quelque chose de mieux spécifiquement à utiliser avec JSON (base-85 est utilisé par postscript etc; mais les avantages sont au mieux marginaux quand vous y pensez)
  • La compression
  • avant l'encodage (et après le décodage) peut avoir beaucoup de sens, selon les données que vous utilisez
7
répondu StaxMan 2010-03-15 06:32:34

étant donné que vous recherchez la possibilité de stocker des données binaires dans un format strictement textuel et très limité, je pense que les frais généraux de Base64 sont minimes par rapport à la commodité que vous attendez de maintenir avec JSON. Si la puissance de traitement et le débit est un problème, alors vous devriez probablement reconsidérer vos formats de fichier.

2
répondu jsoverson 2009-09-18 08:29:14

( Edit 7 ans plus tard: Google Gears est parti. Ignorez cette réponse.)

L'équipe de Google Gears a rencontré le problème du manque-de-binaire-types de données et a tenté de le résoudre:

Blob API

JavaScript a un type de données intégré pour les chaînes de texte, mais rien pour les données binaires. L'objet Blob tente de répondre à cette limitation.

peut-être que tu peux tisser ça d'une façon ou d'une autre.

2
répondu a paid nerd 2016-02-03 23:36:38

juste pour ajouter le point de vue de la ressource et de la complexité à la discussion. Depuis QU'on fait PUT/POST et PATCH pour stocker de nouvelles ressources et les modifier, on devrait se rappeler que le transfert de contenu est une représentation exacte du contenu qui est stocké et qui est reçu en émettant une opération GET.

un message en plusieurs parties est souvent utilisé comme un sauveur, mais pour des raisons de simplicité et pour des tâches plus complexes, je préfère l'idée de donner le contenu dans son ensemble. Il est auto-explication et c'est simple.

et oui JSON est quelque chose d'écrasant mais à la fin JSON lui-même est verbeux. Et le ciel de la cartographie à BASE64 est un moyen de petite.

utiliser correctement les messages multi-parties il faut soit démonter l'objet à envoyer, utiliser un chemin de propriété comme nom de paramètre pour la combinaison automatique ou devra créer un autre protocole/format pour exprimer simplement la charge utile.

aime aussi le BSON cette approche n'est pas aussi répandue et facile à soutenir que l'on voudrait qu'elle le soit.

Fondamentalement, nous manquons juste quelque chose ici, mais l'intégration de données binaires comme base64 est bien établie et la façon d'aller à moins que vous avez vraiment identifié le besoin de faire le transfert binaire réel (qui est à peine souvent le cas).

1
répondu Martin Kersten 2017-12-06 16:55:10

type de données concerne vraiment. J'ai testé différents scénarios sur l'envoi de la charge utile à partir d'une ressource RESTful. Pour l'encodage J'ai utilisé Base64(Apache) et pour la compression GZIP (java.utils.zip.*).La charge utile contient des informations sur le film, une image et un fichier audio. J'ai compressé et encodé les fichiers image et audio qui ont considérablement dégradé les performances. L'encodage avant la compression s'est bien passé. Les images et le contenu audio ont été envoyés sous forme d'octets codés et comprimés [] .

0
répondu Koushik 2012-04-02 16:51:46

Refer: http://snia.org/sites/default/files/Multi-part%20MIME%20Extension%20v1.0g.pdf

décrit un moyen de transférer des données binaires entre un client CDMI et un serveur en utilisant des opérations de type "contenu CDMI" sans avoir besoin de conversion base64 des données binaires.

si vous pouvez utiliser l'opération "non-CDMI content type", il est idéal pour transférer des "données" vers/à partir d'un objet. Les métadonnées peuvent ensuite être ajoutées / récupérées vers/depuis l'objet comme une opération subséquente de type "contenu CDMI".

0
répondu Dheeraj Sangamkar 2013-06-22 05:40:28

si vous utilisez Node, je pense que le moyen le plus efficace et facile est de convertir en UTF16 avec:

Buffer.from(data).toString('utf16le');

vous pouvez récupérer vos données par:

Buffer.from(s, 'utf16le');
0
répondu Sharcoux 2018-06-25 12:19:47

ma solution maintenant, XHR2 utilise ArrayBuffer. Le ArrayBuffer comme séquence binaire contient multipart-contenu, vidéo, audio, graphique, texte et ainsi de suite avec plusieurs types de contenu. Le tout dans Une seule Réponse.

dans le navigateur moderne, ayant DataView, StringView et Blob pour différents composants. Voir aussi: http://rolfrost.de/video.html pour plus de détails.

-1
répondu Rolf Rost 2014-02-11 08:16:57