Formacie JSON natywnie nie obsługuje dane binarne. Dane binarne muszą być poprzedzone znakami ucieczki, aby można je było umieścić w elemencie łańcuchowym (tj. Zero lub więcej znaków Unicode w podwójnych cudzysłowach przy użyciu znaków ucieczki odwrotnego ukośnika) w JSON.
Oczywistą metodą na ucieczkę danych binarnych jest użycie Base64. Jednak Base64 ma wysoki narzut przetwarzania. Rozszerza również 3 bajty na 4 znaki, co prowadzi do zwiększenia rozmiaru danych o około 33%.
Jednym z przypadków użycia jest wersja v0.8 specyfikacji interfejsu API pamięci masowej w chmurze CDMI . Obiekty danych tworzysz za pomocą usługi REST-Webservice za pomocą JSON, np
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=",
}
Czy istnieją lepsze sposoby i standardowe metody kodowania danych binarnych w ciągach JSON?
JSON.parse
Itp. ......Odpowiedzi:
Istnieje 94 znaków Unicode, które mogą być reprezentowane jako jeden bajt zgodnie ze specyfikacją JSON (jeśli twój JSON jest przesyłany jako UTF-8). Mając to na uwadze, myślę, że najlepszym, co możesz zrobić w przestrzeni, jest base85, który reprezentuje cztery bajty jako pięć znaków. Jest to jednak tylko 7% poprawa w stosunku do base64, obliczenia są droższe, a implementacje są mniej powszechne niż w przypadku base64, więc prawdopodobnie nie jest to wygrana.
Możesz także po prostu zamapować każdy bajt wejściowy na odpowiedni znak w U + 0000-U + 00FF, a następnie wykonać minimalne kodowanie wymagane przez standard JSON, aby przekazać te znaki; zaletą jest to, że wymagane dekodowanie jest zero poza wbudowanymi funkcjami, ale wydajność przestrzeni jest zła - zwiększenie o 105% (jeśli wszystkie bajty wejściowe są jednakowo prawdopodobne) w porównaniu z 25% dla base85 lub 33% dla base64.
Ostateczny werdykt: moim zdaniem base64 wygrywa, ponieważ jest to powszechne, łatwe i niezbyt złe , aby uzasadnić zamianę.
Zobacz także: Base91 i Base122
źródło
base64.b85encode()
ib85decode()
teraz. Prosty pomiar czasu kodowania + dekodowania pokazuje, że b85 jest ponad 13 razy wolniejszy niż b64. Mamy więc wygraną o 7%, ale utratę wydajności o 1300%.DEL
się jako znak kontrolny.Natknąłem się na ten sam problem i pomyślałem, że podzielę się rozwiązaniem: dane wieloczęściowe / dane.
Wysyłając wieloczęściowy formularz, najpierw wysyłasz jako ciąg swoje meta-dane JSON , a następnie osobno wysyłasz jako nieprzetworzone pliki binarne (obraz (y), WAV itp.) Indeksowane według nazwy Dyspozycyjności .
Oto fajny samouczek, jak to zrobić w obj-c, a oto artykuł na blogu, który wyjaśnia, w jaki sposób podzielić dane ciągów na granice formularza i oddzielić je od danych binarnych.
Jedyne, co naprawdę musisz zrobić, to po stronie serwera; będziesz musiał przechwycić swoje metadane, które powinny odpowiednio odnosić się do danych binarnych POST (przy użyciu granicy dyspozycji treści).
To prawda, że wymaga dodatkowej pracy po stronie serwera, ale jeśli wysyłasz wiele obrazów lub dużych obrazów, warto. Połącz to z kompresją gzip, jeśli chcesz.
IMHO wysyłanie danych zakodowanych w base64 to włamanie; dane wieloczęściowe / formularze RFC zostały utworzone dla takich problemów jak: wysyłanie danych binarnych w połączeniu z tekstem lub metadanymi.
źródło
Problem z UTF-8 polega na tym, że nie jest to najbardziej efektywne pod względem miejsca kodowanie. Ponadto niektóre losowe binarne sekwencje bajtów mają nieprawidłowe kodowanie UTF-8. Dlatego nie można po prostu interpretować losowej sekwencji bajtów binarnych jako niektórych danych UTF-8, ponieważ będzie to nieprawidłowe kodowanie UTF-8. Zaletą tego ograniczenia kodowania UTF-8 jest to, że sprawia, że jest on solidny i umożliwia lokalizowanie wielobajtowych znaków rozpoczynających i kończących każdy bajt, na który zaczniemy patrzeć.
W konsekwencji, jeśli kodowanie wartości bajtu w zakresie [0..127] wymagałoby tylko jednego bajtu w kodowaniu UTF-8, kodowanie wartości bajtu w zakresie [128..255] wymagałoby 2 bajtów! Gorsze niż to. W JSON, znaki kontrolne, „i \ nie mogą pojawiać się w ciągu znaków. Dane binarne wymagałyby pewnej transformacji, aby były poprawnie zakodowane.
Pokazać. Jeśli przyjmiemy równomiernie rozłożone losowe wartości bajtów w naszych danych binarnych, wówczas średnio połowa bajtów będzie zakodowana w jednym bajcie, a druga połowa w dwóch bajtach. Dane binarne zakodowane w UTF-8 miałyby 150% początkowego rozmiaru.
Kodowanie Base64 rośnie tylko do 133% początkowego rozmiaru. Tak więc kodowanie Base64 jest bardziej wydajne.
Co powiesz na użycie innego kodowania Base? W UTF-8 kodowanie 128 wartości ASCII jest najbardziej wydajne pod względem miejsca. W 8 bitach możesz zapisać 7 bitów. Jeśli więc podzielimy dane binarne na 7-bitowe fragmenty, aby przechowywać je w każdym bajcie łańcucha zakodowanego w UTF-8, zakodowane dane wzrosną tylko do 114% początkowego rozmiaru. Lepsze niż Base64. Niestety nie możemy użyć tej prostej sztuczki, ponieważ JSON nie zezwala na niektóre znaki ASCII. 33 znaki kontrolne ASCII ([0..31] i 127) oraz „i \ muszą zostać wykluczone. Pozostawia nam to tylko 128-35 = 93 znaki.
Teoretycznie moglibyśmy zdefiniować kodowanie Base93, które zwiększyłoby zakodowany rozmiar do 8 / log2 (93) = 8 * log10 (2) / log10 (93) = 122%. Ale kodowanie Base93 nie byłoby tak wygodne jak kodowanie Base64. Base64 wymaga wycięcia wejściowej sekwencji bajtów na 6-bitowe porcje, dla których prosta operacja bitowa działa dobrze. Poza tym 133% to niewiele więcej niż 122%.
Dlatego niezależnie doszedłem do wspólnego wniosku, że Base64 jest rzeczywiście najlepszym wyborem do kodowania danych binarnych w JSON. Moja odpowiedź przedstawia uzasadnienie. Zgadzam się, że nie jest zbyt atrakcyjny z punktu widzenia wydajności, ale rozważam także korzyść z używania JSON z jego czytelną dla człowieka reprezentacją ciągów łatwą do manipulowania we wszystkich językach programowania.
Jeśli wydajność ma krytyczne znaczenie, należy uznać, że zastąpienie JSON powinno polegać wyłącznie na kodowaniu binarnym. Ale z JSON doszedłem do wniosku, że Base64 jest najlepszy.
źródło
BSON (Binary JSON) może działać dla Ciebie. http://en.wikipedia.org/wiki/BSON
Edycja: FYI. Json.net biblioteka .NET obsługuje czytanie i pisanie bson, jeśli szukasz trochę miłości po stronie serwera C #.
źródło
Jeśli masz do czynienia z problemami z przepustowością, spróbuj najpierw skompresować dane po stronie klienta, a następnie base64-it.
Dobrym przykładem takiej magii jest http://jszip.stuartk.co.uk/, a więcej dyskusji na ten temat znajduje się w implementacji JavaScript Gzip
źródło
Content-Encoding
), ponieważ base64 dość dobrze kompresuje.YEnc może pracować dla Ciebie:
http://en.wikipedia.org/wiki/Yenc
Jednak YEnc jest kodowaniem 8-bitowym, więc przechowywanie go w ciągu JSON ma takie same problemy jak przechowywanie oryginalnych danych binarnych - robienie tego naiwnie oznacza około 100% rozszerzenie, co jest gorsze niż base64.
źródło
Chociaż prawdą jest, że base64 ma ~ 33% szybkości ekspansji, niekoniecznie jest prawdą, że narzut przetwarzania jest znacznie większy: to naprawdę zależy od używanej biblioteki / zestawu narzędzi JSON. Kodowanie i dekodowanie to proste, proste operacje, które można nawet zoptymalizować kodowaniem znaków wrt (ponieważ JSON obsługuje tylko UTF-8/16/32) - znaki base64 są zawsze jednobajtowe dla wpisów JSON String. Na przykład na platformie Java są biblioteki, które mogą wykonać to zadanie dość wydajnie, więc narzut wynika głównie z powiększonego rozmiaru.
Zgadzam się z dwiema wcześniejszymi odpowiedziami:
źródło
Format uśmiechu
Kodowanie, dekodowanie i kompaktowanie jest bardzo szybkie
Porównanie prędkości (oparte na Javie, ale mimo to znaczące): https://github.com/eishay/jvm-serializers/wiki/
Jest to także rozszerzenie JSON, które pozwala pominąć kodowanie base64 dla tablic bajtów
Łańcuchy zakodowane w uśmiechu można zgzipować, gdy przestrzeń jest krytyczna
źródło
( Edytuj 7 lat później: Google Gears już nie ma. Zignoruj tę odpowiedź).
Zespół Google Gears napotkał problem braku danych binarnych i próbował rozwiązać ten problem:
Może uda ci się to jakoś utkać.
źródło
Ponieważ szukasz możliwości przekształcenia danych binarnych w format ściśle tekstowy i bardzo ograniczony, myślę, że narzut Base64 jest minimalny w porównaniu z wygodą, jakiej oczekujesz w JSON. Jeśli problem dotyczy mocy obliczeniowej i przepustowości, prawdopodobnie trzeba ponownie rozważyć formaty plików.
źródło
Wystarczy dodać do dyskusji punkt widzenia dotyczący zasobów i złożoności. Ponieważ wykonując PUT / POST i PATCH do przechowywania nowych zasobów i ich modyfikowania, należy pamiętać, że transfer zawartości jest dokładną reprezentacją treści, która jest przechowywana i która jest odbierana przez wykonanie operacji GET.
Wiadomość wieloczęściowa jest często używana jako wybawiciel, ale ze względu na prostotę i przy bardziej złożonych zadaniach wolę pomysł podania treści jako całości. Jest to oczywiste i proste.
I tak, JSON jest czymś paraliżującym, ale ostatecznie sam JSON jest pełny. A narzut związany z mapowaniem do BASE64 jest sposobem na mały.
Używając poprawnie komunikatów wieloczęściowych, należy albo zdemontować obiekt do wysłania, użyć ścieżki właściwości jako nazwy parametru dla automatycznej kombinacji, albo trzeba będzie utworzyć inny protokół / format, aby wyrazić ładunek.
Podobnie jak podejście BSON, nie jest to tak szeroko i łatwo wspierane, jak by się chciało.
Zasadniczo po prostu coś przeoczyliśmy, ale osadzanie danych binarnych, ponieważ base64 jest dobrze ugruntowane i jest gotowe, chyba że naprawdę zidentyfikujesz potrzebę wykonania prawdziwego transferu binarnego (co rzadko zdarza się tak często).
źródło
Kopię trochę więcej (podczas implementacji base128 ) i ujawniam , że kiedy wysyłamy znaki, których kody ascii są większe niż 128, wtedy przeglądarka (chrome) w rzeczywistości wysyła DWIE znaki (bajty) zamiast jednego :( . Powodem jest to, że JSON przez defaul użyj znaków utf8, dla których znaki o kodach ascii powyżej 127 są kodowane dwoma bajtami, o czym wspomniano w odpowiedzi chmike . Wykonałem test w ten sposób: wpisz chrome url bar chrome: // net-export / , zaznacz "Uwzględnij raw bytes ", rozpocznij przechwytywanie, wysyłaj żądania POST (używając fragmentu kodu u dołu), zatrzymaj przechwytywanie i zapisz plik json z danymi surowych żądań. Następnie przeglądamy ten plik json:
4142434445464748494a4b4c4d4e
jest kodem szesnastkowym,ABCDEFGHIJKLMN
i przekonamy się"byte_count": 639
o tym.C2BCC2BDC380C381C382C383C384C385C386C387C388C389C38AC38B
Są to kody znaków w kodzie znaków UTP -hex¼½ÀÁÂÃÄÅÆÇÈÉÊË
(jednak kody szesnastkowe ascii tych znaków toc1c2c3c4c5c6c7c8c9cacbcccdce
). Tak"byte_count": 703
więc jest 64 bajtów dłuższy niż żądanie base64, ponieważ znaki o kodach ascii powyżej 127 są kodowane przez 2 bajty w żądaniu :(W rzeczywistości nie mamy zysków z wysyłania znaków o kodach> 127 :(. W przypadku ciągów base64 nie obserwujemy takich negatywnych zachowań (prawdopodobnie również w przypadku base85 - nie sprawdzam) - jednak może być jakieś rozwiązanie tego problemu wysyłanie danych w binarnej części danych wieloczęściowych / formularzy POST opisanych w odpowiedzi Ælex (jednak zwykle w tym przypadku nie musimy wcale używać żadnego podstawowego kodowania ...).
Alternatywne podejście może polegać na odwzorowaniu dwubajtowej części danych na jeden poprawny znak utf8 przez kod za pomocą czegoś takiego jak base65280 / base65k, ale prawdopodobnie byłby mniej skuteczny niż base64 ze względu na specyfikację utf8 ...
Pokaż fragment kodu
źródło
Typ danych naprawdę dotyczy. Testowałem różne scenariusze wysyłania ładunku z zasobu RESTful. Do kodowania użyłem Base64 (Apache), a do kompresji GZIP (java.utils.zip. *). Ładunek zawiera informacje o filmie, obrazie i pliku audio. Skompresowałem i zakodowałem pliki obrazu i dźwięku, co drastycznie obniżyło wydajność. Kodowanie przed kompresją okazało się dobre. Treści obrazu i dźwięku zostały przesłane jako zakodowane i skompresowane bajty [].
źródło
Odnosić się: http://snia.org/sites/default/files/Multi-part%20MIME%20Extension%20v1.0g.pdf
Opisuje sposób przesyłania danych binarnych między klientem CDMI a serwerem za pomocą operacji „Typ zawartości CDMI” bez konieczności konwersji danych binarnych base64.
Jeśli możesz użyć operacji „Typ zawartości innej niż CDMI”, idealnie jest przenieść „dane” do / z obiektu. Metadane mogą być później dodane / pobrane do / z obiektu jako kolejna operacja typu zawartości CDMI.
źródło
Moje rozwiązanie teraz, XHR2 używa ArrayBuffer. ArrayBuffer jako sekwencja binarna zawiera zawartość wieloczęściową, wideo, audio, grafikę, tekst itd. Z wieloma typami treści. Wszystko w jednej odpowiedzi.
W nowoczesnej przeglądarce mają DataView, StringView i Blob dla różnych komponentów. Zobacz także: http://rolfrost.de/video.html, aby uzyskać więcej informacji.
źródło
[16, 2, 38, 89]
co jest bardzo nieefektywne.