Двоичные данные в строке JSON. Что-то лучше, чем Base64 - PullRequest
550 голосов
/ 18 сентября 2009

Формат JSON изначально не поддерживает двоичные данные. Двоичные данные должны быть экранированы, чтобы их можно было поместить в строковый элемент (т. Е. Ноль или более символов Unicode в двойных кавычках с использованием обратной косой черты) в JSON.

Очевидный способ избежать двоичных данных - использовать Base64. Тем не менее, Base64 имеет большие накладные расходы на обработку. Также он расширяет 3 байта в 4 символа, что приводит к увеличению размера данных примерно на 33%.

Одним из вариантов использования этого является черновой вариант версии 0.8 спецификации API облачного хранилища CDMI *1008*. Вы создаете объекты данных через REST-Webservice, используя JSON, например,

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

Существуют ли лучшие способы и стандартные методы для кодирования двоичных данных в строки JSON?

Ответы [ 16 ]

415 голосов
/ 18 сентября 2009

Существует 94 символа Unicode, которые могут быть представлены одним байтом в соответствии со спецификацией JSON (если ваш JSON передается как UTF-8). Имея это в виду, я думаю, что лучшее, что вы можете сделать в пространстве, это base85 , который представляет четыре байта в виде пяти символов. Тем не менее, это всего лишь 7% улучшение по сравнению с base64, его вычисление обходится дороже, а реализации встречаются реже, чем для base64, поэтому, вероятно, это не победа.

Вы также можете просто сопоставить каждый входной байт с соответствующим символом в U + 0000-U + 00FF, а затем выполнить минимальную кодировку, требуемую стандартом JSON для передачи этих символов; Преимущество здесь в том, что требуемое декодирование равно нулю по сравнению со встроенными функциями, но эффективность использования пространства плохая - расширение 105% (если все входные байты одинаково вероятны) против 25% для base85 или 33% для base64.

Окончательный вердикт: base64 выигрывает, на мой взгляд, на том основании, что это обычное, простое и неплохое достаточно , чтобы оправдать замену.

См. Также: Base91

215 голосов
/ 22 января 2015

Я столкнулся с той же проблемой и решил поделиться решением: multipart / form-data.

Отправляя многокомпонентную форму, вы сначала отправляете в виде строки свои метаданные JSON , а затем отдельно отправляете как необработанный двоичный файл (изображения, файлы формата wavs и т. Д.), Проиндексированные Content- Распоряжение имя.

Вот хороший учебник о том, как это сделать в obj-c, а вот статья блога , в которой объясняется, как разделить строковые данные с границей формы, и отделить это из двоичных данных.

Единственное изменение, которое вам действительно нужно сделать, это на стороне сервера; вам нужно будет захватить ваши метаданные, которые должны соответствующим образом ссылаться на двоичные данные POST (используя границу Content-Disposition).

Конечно, это требует дополнительной работы на стороне сервера, но если вы отправляете много изображений или большие изображения, это того стоит. Объедините это со сжатием gzip, если хотите.

ИМХО отправка данных в кодировке base64 - это взлом; RFC multipart / form-data был создан для таких проблем: отправка двоичных данных в сочетании с текстом или метаданными.

31 голосов
/ 21 сентября 2011

BSON (Binary JSON) может работать для вас. http://en.wikipedia.org/wiki/BSON

Edit: К вашему сведению, библиотека .NET json.net поддерживает чтение и запись bson, если вы ищете какую-нибудь любовь на C # на стороне сервера.

27 голосов
/ 22 сентября 2013

Проблема с UTF-8 заключается в том, что это не самая экономичная кодировка. Кроме того, некоторые случайные двоичные байтовые последовательности недопустимы в кодировке UTF-8. Таким образом, вы не можете просто интерпретировать случайную двоичную последовательность байтов как некоторые данные UTF-8, потому что это будет недопустимой кодировкой UTF-8. Преимущество этого ограничения на кодировку UTF-8 состоит в том, что он делает надежным и возможным обнаружение многобайтовых символов начала и конца любого байта, на который мы начинаем смотреть.

Как следствие, если для кодирования значения байта в диапазоне [0..127] потребуется только один байт в кодировке UTF-8, для кодирования значения байта в диапазоне [128..255] потребуется 2 байта! Хуже этого. В JSON управляющие символы "и \ не могут появляться в строке. Поэтому для правильного кодирования двоичных данных потребуется некоторое преобразование.

Давай посмотрим. Если мы примем равномерно распределенные случайные байтовые значения в наших двоичных данных, то в среднем половина байтов будет закодирована в один байт, а другая половина - в два байта. У двоичных данных в кодировке UTF-8 будет 150% от исходного размера.

Кодировка Base64 увеличивается только до 133% от исходного размера. Таким образом, кодировка Base64 более эффективна.

А как насчет использования другой базовой кодировки? В UTF-8 кодирование 128 значений ASCII является наиболее эффективным с точки зрения места. В 8 битах вы можете хранить 7 бит. Поэтому, если мы разрежем двоичные данные на 7-битные порции, чтобы сохранить их в каждом байте строки в кодировке UTF-8, кодированные данные вырастут только до 114% от исходного размера. Лучше, чем Base64. К сожалению, мы не можем использовать этот простой трюк, потому что JSON не допускает некоторые символы ASCII. 33 управляющих символа ASCII ([0..31] и 127) и символы "и \" должны быть исключены. Это оставляет нам только 128-35 = 93 символа.

Таким образом, теоретически мы могли бы определить кодировку Base93, которая бы увеличивала кодированный размер до 8 / log2 (93) = 8 * log10 (2) / log10 (93) = 122%. Но кодировка Base93 будет не такой удобной, как кодировка Base64. Base64 требует разрезать последовательность входных байтов на 6-битные порции, для которых хорошо работает простая побитовая операция. При этом 133% - это не намного больше, чем 122%.

Вот почему я независимо пришел к общему выводу, что Base64 действительно лучший выбор для кодирования двоичных данных в JSON. Мой ответ оправдывает это. Я согласен, что это не очень привлекательно с точки зрения производительности, но учту также преимущество использования JSON с его удобочитаемым представлением строк, которым легко манипулировать на всех языках программирования.

Если производительность критична, то чистая двоичная кодировка должна рассматриваться как замена JSON. Но с JSON я пришел к выводу, что Base64 - лучший.

18 голосов
/ 15 марта 2011

Если вы столкнулись с проблемами пропускной способности, попробуйте сначала сжать данные на стороне клиента, а затем base64-it.

Хороший пример такой магии - http://jszip.stuartk.co.uk/, а дальнейшее обсуждение этой темы - Реализация JavaScript в Gzip

17 голосов
/ 18 сентября 2009

yEnc может работать для вас:

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

"yEnc - это схема кодирования двоичного текста для передачи двоичного файлы в [текст]. Это снижает накладные расходы по сравнению с предыдущими на основе US-ASCII методы кодирования с использованием 8-битного расширенного метода кодирования ASCII. Часто возникают накладные расходы yEnc (если значение каждого байта отображается приблизительно с той же частотой в среднем) всего 1-2%, по сравнению с Затраты 33% –40% для 6-битных методов кодирования, таких как uuencode и Base64. ... К 2003 году yEnc стал де-факто стандартной системой кодирования для двоичные файлы в Usenet. "

Однако yEnc - это 8-битная кодировка, поэтому хранение ее в строке JSON сопряжено с теми же проблемами, что и сохранение исходных двоичных данных - выполнение этого наивного способа означает примерно 100% расширение, что хуже, чем base64.

9 голосов
/ 07 января 2012

Формат улыбки

Кодирование, декодирование и сжатие очень быстрое

Сравнение скорости (на основе Java, но, тем не менее, имеет смысл): https://github.com/eishay/jvm-serializers/wiki/

Также это расширение JSON, позволяющее пропустить кодировку base64 для байтовых массивов

Строки, закодированные в Smile, могут быть сжаты в случае критического места

7 голосов
/ 15 марта 2010

Несмотря на то, что base64 имеет степень расширения ~ 33%, не обязательно верно, что накладные расходы обработки значительно больше, чем это: это действительно зависит от используемой вами библиотеки / инструментария JSON. Кодирование и декодирование - это простые прямые операции, и их можно даже оптимизировать по кодированию символов (поскольку JSON поддерживает только UTF-8/16/32) - символы base64 всегда являются однобайтовыми для записей строки JSON. Например, на платформе Java есть библиотеки, которые могут выполнять эту работу довольно эффективно, так что накладные расходы в основном связаны с расширенным размером.

Я согласен с двумя предыдущими ответами:

  • base64 - это простой, широко используемый стандарт, поэтому вряд ли найдется что-то более конкретное для использования с JSON (base-85 используется postscript и т. Д .; но преимущества в лучшем случае незначительны, если подумать)
  • сжатие перед кодированием (и после декодирования) может иметь много смысла, в зависимости от данных, которые вы используете
4 голосов
/ 18 сентября 2009

( Редактировать 7 лет спустя: Google Gears больше нет. Игнорировать этот ответ.)


Команда Google Gears столкнулась с проблемой отсутствия типов двоичных данных и попыталась решить ее:

Blob API

JavaScript имеет встроенный тип данных для текстовых строк, но ничего для двоичных данных. Объект Blob пытается устранить это ограничение.

Может быть, вы можете каким-то образом сплести это.

3 голосов
/ 18 сентября 2009

Поскольку вам нужна возможность вводить двоичные данные в строго текстовый и очень ограниченный формат, я думаю, что издержки Base64 минимальны по сравнению с удобством, которое вы ожидаете поддерживать с JSON. Если вычислительная мощность и пропускная способность являются проблемой, вам, вероятно, придется пересмотреть форматы файлов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...