791 Stimmen

Binäre Daten in JSON-String. Etwas Besseres als Base64

があります。 JSON-Format unterstützt von Haus aus keine Binärdaten. Die binären Daten müssen escaped werden, damit sie in ein String-Element (d. h. null oder mehr Unicode-Zeichen in Anführungszeichen mit Backslash-Escapes) in JSON eingefügt werden können.

Eine offensichtliche Methode zur Entschlüsselung von Binärdaten ist die Verwendung von Base64. Base64 ist jedoch mit einem hohen Verarbeitungsaufwand verbunden. Außerdem werden 3 Bytes in 4 Zeichen expandiert, was zu einer Erhöhung der Datengröße um etwa 33 % führt.

Ein Anwendungsfall hierfür ist der Entwurf v0.8 des Spezifikation der CDMI-Wolkenspeicher-API . Sie erstellen Datenobjekte über einen REST-Webservice unter Verwendung von JSON, z.B.

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=",
}

Gibt es bessere Möglichkeiten und Standardmethoden zur Kodierung von Binärdaten in JSON-Strings?

51 Stimmen

Für den Upload: Sie machen es nur einmal, also ist es keine große Sache. Für den Download: Sie werden überrascht sein, wie gut base64 komprimiert unter gzip Wenn Sie also gzip auf Ihrem Server aktiviert haben, ist das wahrscheinlich auch in Ordnung.

6 Stimmen

Eine weitere würdige Lösung msgpack.org für die Hardcore-Nerds: github.com/msgpack/msgpack/blob/master/spec.md

6 Stimmen

@cloudfeet, Einmal pro Benutzer pro Aktion . Eine sehr große Sache.

577voto

hobbs Punkte 204816

Es gibt 94 Unicode-Zeichen, die nach der JSON-Spezifikation als ein Byte dargestellt werden können (wenn Ihr JSON als UTF-8 übertragen wird). In Anbetracht dessen denke ich, dass das Beste, was Sie platztechnisch tun können, ist Basis85 was vier Bytes als fünf Zeichen darstellt. Dies ist jedoch nur eine 7 %ige Verbesserung gegenüber base64, die Berechnung ist teurer, und Implementierungen sind weniger verbreitet als bei base64, so dass es wahrscheinlich kein Gewinn ist.

Sie könnten auch einfach jedes Eingabebyte auf das entsprechende Zeichen in U+0000-U+00FF abbilden und dann die vom JSON-Standard geforderte Mindestkodierung durchführen, um diese Zeichen weiterzugeben; der Vorteil hier ist, dass die erforderliche Dekodierung über die eingebauten Funktionen hinaus gleich Null ist, aber die Raumeffizienz ist schlecht - eine 105%ige Expansion (wenn alle Eingabebytes gleich wahrscheinlich sind) gegenüber 25% für base85 oder 33% für base64.

Endgültige Entscheidung: base64 gewinnt meiner Meinung nach, weil es verbreitet, einfach und nicht schlecht ist. genug um einen Austausch zu rechtfertigen.

Siehe auch: Basis91 et Basis122

0 Stimmen

Wäre das nicht effizienter? Ich verstehe nicht, wie Base-85 das Beste sein kann.

0 Stimmen

Wie hängt die Kodierung, von der Sie im zweiten Absatz sprechen, mit der Kodierung hier beschrieben ?

8 Stimmen

Warten Sie, wie ist nur mit dem tatsächlichen Byte während der Codierung der Anführungszeichen eine 105% Erweiterung und base64 nur 33%? Ist base64 nicht 133%?

337voto

Ælex Punkte 13685

Ich bin auf das gleiche Problem gestoßen und dachte, ich könnte eine Lösung anbieten: multipart/form-data.

Beim Senden eines mehrteiligen Formulars senden Sie zunächst als String Ihre JSON-Metadaten und dann separat als Rohbinärdatei (Bild(er), wavs, etc.), indiziert durch den Inhalt-Disposition Name.

Hier ist eine schöne Lehrgang wie man das in Obj-C macht, und hier ist ein Blog-Artikel die erklärt, wie man die Zeichenkettendaten mit der Formulargrenze partitioniert und von den Binärdaten trennt.

Die einzige Änderung, die Sie wirklich vornehmen müssen, ist auf der Serverseite; Sie müssen Ihre Metadaten erfassen, die die POST'ed-Binärdaten entsprechend referenzieren sollten (durch Verwendung einer Content-Disposition-Grenze).

Zugegeben, es erfordert zusätzliche Arbeit auf der Serverseite, aber wenn Sie viele oder große Bilder versenden, ist es das wert. Kombinieren Sie dies mit gzip-Komprimierung, wenn Sie möchten.

IMHO ist das Senden von base64-kodierten Daten ein Hack; der RFC multipart/form-data wurde für Probleme wie dieses geschaffen: Senden von Binärdaten in Kombination mit Text oder Metadaten.

5 Stimmen

Übrigens, die Google Drive API macht es auf diese Weise: developers.google.com/drive/v2/reference/files/update#examples

12 Stimmen

Warum ist diese Antwort so weit unten, wenn sie native Funktionen verwendet, anstatt zu versuchen, einen runden (binären) Stift in ein eckiges (ASCII) Loch zu stecken?...

9 Stimmen

das Senden von base64 kodierten Daten ist ein Hack ebenso wie multipart/form-data. Selbst in dem von Ihnen verlinkten Blog-Artikel heißt es, dass Durch die Verwendung des Content-Typs multipart/form-data geben Sie an, dass es sich bei dem, was Sie senden, um ein Formular handelt. Das ist es aber nicht. Ich denke also, dass der base64-Hack nicht nur viel einfacher zu implementieren, sondern auch zuverlässiger ist Ich habe einige Bibliotheken gesehen (z.B. für Python), die den Inhaltstyp multipart/form-data fest einkodiert hatten.

49voto

chmike Punkte 19151

Das Problem mit UTF-8 ist, dass es nicht die platzsparendste Kodierung ist. Außerdem sind einige zufällige binäre Bytefolgen in der UTF-8-Kodierung ungültig. Man kann also nicht einfach eine zufällige binäre Bytefolge als UTF-8-Daten interpretieren, weil sie eine ungültige UTF-8-Kodierung ist. Der Vorteil dieser Einschränkung der UTF-8-Kodierung besteht darin, dass sie robust ist und es möglich macht, Mehrbyte-Zeichen am Anfang und am Ende zu lokalisieren, unabhängig davon, welches Byte wir zu Beginn betrachten.

Dies hat zur Folge, dass die Kodierung eines Byte-Wertes im Bereich [0..127] bei der UTF-8-Kodierung nur ein Byte benötigt, während die Kodierung eines Byte-Wertes im Bereich [128..255] 2 Bytes erfordert! Und es kommt noch schlimmer. In JSON dürfen Steuerzeichen, " und \ nicht in einer Zeichenfolge erscheinen. Die binären Daten müssten also umgewandelt werden, um richtig kodiert zu werden.

Mal sehen. Wenn wir von gleichmäßig verteilten, zufälligen Byte-Werten in unseren Binärdaten ausgehen, würde im Durchschnitt die Hälfte der Bytes in einem Byte und die andere Hälfte in zwei Bytes kodiert werden. Die UTF-8-kodierten Binärdaten hätten dann 150 % der ursprünglichen Größe.

Die Base64-Kodierung wächst nur auf 133 % der ursprünglichen Größe. Die Base64-Kodierung ist also effizienter.

Wie wäre es mit der Verwendung einer anderen Base-Kodierung? In UTF-8 ist die Kodierung der 128 ASCII-Werte am platzsparendsten. In 8 Bits können Sie 7 Bits speichern. Wenn wir also die binären Daten in 7-Bit-Stücke zerschneiden, um sie in jedem Byte einer UTF-8-kodierten Zeichenfolge zu speichern, würden die kodierten Daten nur auf 114 % der ursprünglichen Größe anwachsen. Besser als Base64. Leider können wir diesen einfachen Trick nicht anwenden, da JSON einige ASCII-Zeichen nicht zulässt. Die 33 Steuerzeichen von ASCII ( [0..31] und 127) und die " und \ müssen ausgeschlossen werden. Damit bleiben uns nur 128-35 = 93 Zeichen.

Theoretisch könnten wir also eine Base93-Kodierung definieren, die die kodierte Größe auf 8/log2(93) = 8*log10(2)/log10(93) = 122% erhöhen würde. Aber eine Base93-Kodierung wäre nicht so praktisch wie eine Base64-Kodierung. Bei Base64 muss die eingegebene Bytefolge in 6-Bit-Blöcke zerlegt werden, wofür eine einfache bitweise Operation gut geeignet ist. Außerdem ist 133% nicht viel mehr als 122%.

Deshalb bin ich unabhängig davon zu dem gemeinsamen Schluss gekommen, dass Base64 tatsächlich die beste Wahl für die Kodierung von Binärdaten in JSON ist. Meine Antwort enthält eine Rechtfertigung dafür. Ich stimme zu, dass es aus Sicht der Leistung nicht sehr attraktiv ist, aber bedenken Sie auch den Vorteil der Verwendung von JSON mit seiner menschenlesbaren Zeichenkettendarstellung, die in allen Programmiersprachen leicht zu bearbeiten ist.

Wenn die Leistung entscheidend ist, sollte eine rein binäre Kodierung als Ersatz für JSON in Betracht gezogen werden. Aber mit JSON ist meine Schlussfolgerung, dass Base64 die beste ist.

0 Stimmen

Wie wäre es mit Base128, aber dann lassen Sie den JSON-Serialisierer die " und \ entkommen? Ich denke, es ist vernünftig, vom Benutzer zu erwarten, dass er eine JSON-Parser-Implementierung verwendet.

1 Stimmen

@jcalfee314 leider ist dies nicht möglich, da Zeichen mit ASCII-Code unter 32 in JSON-Strings nicht zulässig sind. Kodierungen mit einer Basis zwischen 64 und 128 wurden bereits definiert, aber der erforderliche Rechenaufwand ist höher als bei base64. Der Gewinn an kodierter Textgröße ist es nicht wert.

0 Stimmen

Wenn eine große Menge an Bildern in base64 geladen wird (sagen wir 1000), oder über eine wirklich langsame Verbindung, würde base85 oder base93 jemals für den reduzierten Netzwerkverkehr (mit oder ohne gzip) bezahlen? Ich bin neugierig, ob es einen Punkt gibt, an dem die kompakteren Daten für eine der alternativen Methoden sprechen würden.

46voto

DarcyThomas Punkte 1068

BSON (Binary JSON) könnte für Sie geeignet sein. http://en.wikipedia.org/wiki/BSON

Bearbeiten: Zu Ihrer Information: Die .NET-Bibliothek json.net unterstützt das Lesen und Schreiben von bson, wenn Sie auf der Suche nach etwas C# Server Seite Liebe sind.

3 Stimmen

"In einigen Fällen wird BSON aufgrund der Längenpräfixe und der expliziten Array-Indizes mehr Platz als JSON benötigen." de.wikipedia.org/wiki/BSON

2 Stimmen

Die gute Nachricht: BSON unterstützt nativ Typen wie Binary, Datetime und ein paar andere (besonders nützlich, wenn Sie MongoDB verwenden). Schlechte Nachricht: Die Kodierung ist binäre Bytes... also ist es eine Nicht-Antwort auf die OP. Es wäre jedoch nützlich über einen Kanal, der Binärdaten nativ unterstützt, wie z.B. RabbitMQ message, ZeroMQ message oder ein benutzerdefinierter TCP- oder UDP-Socket.

23voto

andrej Punkte 3886

Wenn Sie mit Bandbreitenproblemen zu kämpfen haben, versuchen Sie, die Daten zuerst auf der Client-Seite zu komprimieren und dann base64-it.

Ein schönes Beispiel für solche Magie finden Sie unter http://jszip.stuartk.co.uk/ und weitere Diskussionen zu diesem Thema finden Sie unter JavaScript-Implementierung von Gzip

2 Stimmen

Hier ist eine JavaScript-Zip-Implementierung, die eine bessere Leistung verspricht: zip.js

0 Stimmen

Beachten Sie, dass Sie auch danach noch komprimieren können (und sollten) (normalerweise über Content-Encoding ), da base64 ziemlich gut komprimiert.

0 Stimmen

@MahmoudAl-Qudsi Sie meinten, dass Sie base64(zip(base64(zip(data))))? Ich bin mir nicht sicher, ob es eine gute Idee ist, ein weiteres zip hinzuzufügen und es dann base64 zu machen (um es als Daten senden zu können).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X