البيانات الثنائية في سلسلة JSON.شيء أفضل من Base64

StackOverflow https://stackoverflow.com/questions/1443158

  •  22-07-2019
  •  | 
  •  

سؤال

ال تنسيق جيسون أصلاً لا يدعم البيانات الثنائية.يجب أن يتم الهروب من البيانات الثنائية بحيث يمكن وضعها في عنصر سلسلة (أي.صفر أو أكثر من أحرف Unicode في علامات اقتباس مزدوجة باستخدام هروب الشرطة المائلة العكسية) في JSON.

إحدى الطرق الواضحة للهروب من البيانات الثنائية هي استخدام Base64.ومع ذلك، فإن Base64 لديه حمل معالجة مرتفع.كما أنه يوسع 3 بايت إلى 4 أحرف مما يؤدي إلى زيادة حجم البيانات بحوالي 33%.

إحدى حالات الاستخدام لهذا هي مسودة الإصدار v0.8 من مواصفات API للتخزين السحابي لـ CDMI.يمكنك إنشاء كائنات بيانات عبر خدمة 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؟

هل كانت مفيدة؟

المحلول

وهناك 94 أحرف Unicode التي يمكن أن تكون ممثلة على النحو بايت واحد وفقا لمواصفات JSON (إذا ينتقل JSON الخاص بك كما UTF-8). مع أخذ ذلك في الاعتبار، وأعتقد أن أفضل ما يمكن القيام به في الفضاء الحكمة هو base85 التي تمثل أربعة بايت كما خمسة أحرف. ومع ذلك، هذه ليست سوى تحسين 7٪ خلال base64 في، انها أكثر تكلفة لحساب، وتطبيقات هي أقل شيوعا من لbase64 في ذلك هو على الأرجح ليس الفوز.

ويمكنك أيضا أن مجرد تعيين كل بايت مساهمة في الحرف المطابق في U + 0000-U + 00FF، ثم القيام الحد الأدنى الترميز المطلوب وفق المعايير JSON لتمرير تلك الأحرف. والميزة هنا هي أن فك المطلوب هو شيء أبعد من وظائف مدمج، ولكن كفاءة الفضاء سيئة - توسع 105٪ (إذا كان كل بايت مدخلات هي على الأرجح على قدم المساواة) مقابل 25٪ للbase85 أو 33٪ لbase64 في

والحكم النهائي: فوز base64 في، في رأيي، على أساس أنه أمر شائع، وسهلة، وليس سيئا <م> بما فيه الكفاية لتبرير استبدال

وانظر أيضا: Base91

نصائح أخرى

لقد واجهت نفس المشكلة، واعتقدت أنني سأشارك الحل: بيانات متعددة الأجزاء/النموذج.

عن طريق إرسال نموذج متعدد الأجزاء، فإنك ترسله أولاً كسلسلة بيانات تعريف JSON, ، ثم أرسل بشكل منفصل كثنائي خام (صورة (صور)، wavs، إلخ) مفهرسة بواسطة ملف التصرف في المحتوى اسم.

هنا لطيفة درس تعليمي حول كيفية القيام بذلك في obj-c، وهنا مقال مدونة يشرح كيفية تقسيم بيانات السلسلة مع حدود النموذج وفصلها عن البيانات الثنائية.

التغيير الوحيد الذي عليك فعله حقًا هو من جانب الخادم؛سيتعين عليك التقاط بيانات التعريف الخاصة بك والتي يجب أن تشير إلى بيانات POST الثنائية بشكل مناسب (باستخدام حدود التصرف في المحتوى).

من المؤكد أن الأمر يتطلب عملاً إضافيًا من جانب الخادم، ولكن إذا كنت ترسل العديد من الصور أو الصور الكبيرة، فهذا يستحق ذلك.ادمج هذا مع ضغط gzip إذا كنت تريد ذلك.

يعد إرسال IMHO للبيانات المشفرة باستخدام Base64 بمثابة اختراق؛تم إنشاء بيانات النموذج/الأجزاء المتعددة RFC لمشكلات مثل هذه:إرسال البيانات الثنائية مع النص أو البيانات الوصفية.

وBSON (ثنائي JSON) قد عمل لك. http://en.wikipedia.org/wiki/BSON

وتحرير: لمعلوماتك المكتبة json.net يدعم القراءة والكتابة bson إذا كنت تبحث عن بعض الحب C # جانب الملقم.

والمشكلة مع 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، ترميز قيم ASCII 128 هي المساحة الأكبر كفاءة. في 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 في خفض تسلسل المدخلات بايت في قطع 6bit التي عملية المختصة بالبت بسيطة تعمل بشكل جيد. بجانب 133٪ ليست أكثر بكثير من 122٪.

وهذا هو السبب في أنني جاء بشكل مستقل إلى استنتاج شائع بأن باستخدام Base64 هو في الواقع أفضل خيار لتشفير البيانات الثنائية في JSON. تقدم جوابي مبررا لذلك. وأنا أتفق أنها ليست جذابة للغاية من وجهة نظر الأداء، ولكن أيضا أن تنظر في الاستفادة من استخدام JSON مع انها البشري تمثيل سلسلة قراءة السهل التلاعب في جميع لغات البرمجة.

وإذا كان الأداء أهمية من ينبغي النظر في الترميز الثنائي نقية كما استبدال JSON. ولكن مع JSON استنتاجي هو أن باستخدام Base64 هو الأفضل.

إذا كنت تتعامل مع مشاكل عرض النطاق الترددي، في محاولة لضغط البيانات على جانب العميل أولا، ثم base64 في عليه.

مثال لطيف من هذا السحر هو في http://jszip.stuartk.co.uk/ ومزيد من النقاش لهذا الموضوع هو في جافا سكريبت تنفيذ ببرنامج Gzip

وyEnc قد عمل لك:

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

<اقتباس فقرة>   

و"yEnc هو نظام ترميز ثنائي إلى نص لنقل ثنائي   الملفات في [النص]. فهو يقلل من النفقات العامة على مدى السابق US-ASCII المستندة إلى   أساليب الترميز باستخدام ASCII الموسعة طريقة ترميز 8 بت.   النفقات العامة yEnc هو في كثير من الأحيان (في حالة ظهور كل قيمة بايت تقريبا   مع نفس التردد في المتوسط) ما لا يزيد عن 1-2٪، مقارنة مع   33٪ -40٪ النفقات العامة للطرق ترميز 6 بت مثل إلغاء ترميز وباستخدام Base64.   ... وبحلول عام 2003 أصبح yEnc الفعلي نظام ترميز موحد ل   الملفات الثنائية على الاعضاء ".

ولكن، yEnc هو ترميز 8 بت، وبالتالي تخزينه في سلسلة JSON لديها نفس المشاكل تخزين البيانات الثنائية الأصلي - فعل ذلك بالطريقة الساذجة يعني حوالي توسع 100٪، وهو أسوأ من base64 في <. / P>

ابتسامة شكل

وانها سريعة جدا لتشفير، فك والمدمجة

ومقارنة سرعة (جافا مقرها لكن معنى ذلك): https://github.com/eishay/jvm-serializers/ ويكي /

وأيضا انها امتداد لJSON التي تسمح لك لتخطي base64 ترميز للصفائف بايت

وابتسامة المشفرة سلاسل يمكن ملف GZipped عندما تكون مساحة أمر بالغ الأهمية

في حين أنه من الصحيح أن Base64 لديه معدل توسع يصل إلى 33% تقريبًا، إلا أنه ليس صحيحًا بالضرورة أن تكلفة المعالجة أكبر بكثير من هذا:يعتمد الأمر حقًا على مكتبة/مجموعة أدوات JSON التي تستخدمها.يعد التشفير وفك التشفير عمليتين بسيطتين ومباشرتين، ويمكن أيضًا تحسين ترميز أحرف WRT (حيث أن JSON يدعم UTF-8/16/32 فقط) - تكون أحرف base64 دائمًا أحادية البايت لإدخالات سلسلة JSON.على سبيل المثال، توجد مكتبات في نظام Java الأساسي يمكنها القيام بهذه المهمة بكفاءة إلى حد ما، لذا فإن الحمل الزائد يرجع في الغالب إلى الحجم الموسع.

وأنا أتفق مع إجابتين سابقتين:

  • Base64 هو معيار بسيط وشائع الاستخدام، لذلك من غير المرجح أن تجد شيئًا أفضل لاستخدامه على وجه التحديد مع JSON (يتم استخدام Base-85 بواسطة التذييل وما إلى ذلك؛لكن الفوائد تكون هامشية في أحسن الأحوال عندما تفكر في الأمر)
  • الضغط قبل التشفير (وبعد فك التشفير) قد يكون له معنى كبير، اعتمادًا على البيانات التي تستخدمها

(تحرير بعد 7 سنوات: لقد اختفى جوجل جيرز.تجاهل هذه الإجابة.)


واجه فريق Google Gears مشكلة نقص أنواع البيانات الثنائية وحاول حلها:

واجهة برمجة تطبيقات النقطة

تحتوي JavaScript على نوع بيانات مضمن للسلاسل النصية، ولكن لا يوجد شيء للبيانات الثنائية.يحاول كائن Blob معالجة هذا القيد.

ربما يمكنك نسج ذلك بطريقة أو بأخرى.

ومنذ كنت تبحث عن القدرة على نعل البيانات الثنائية في شكل صارم يستند إلى نص ومحدودة للغاية، وأعتقد أن النفقات العامة باستخدام Base64 هو الحد الأدنى بالمقارنة مع راحة كنت تتوقع للحفاظ مع JSON. إذا تجهيز الطاقة والإنتاجية هو مصدر قلق، فأنت على الأرجح بحاجة إلى إعادة النظر في صيغ الملفات الخاصة بك.

وفقط لإضافة الموارد وتعقيد جهة نظر في المناقشة. منذ قيام PUT / وظيفة وPATCH لتخزين موارد جديدة وتعديلهم، يجب أن نتذكر أن نقل المحتوى هو التمثيل الدقيق للمحتوى الذي تم تخزينه وأن استلام بإصدار عملية GET.

وكثيرا ما يستخدم رسالة متعددة الأجزاء كمنقذ ولكن لسبب والبساطة ولمزيد من المهام المعقدة، لكنني أفضل فكرة إعطاء مضمون ككل. فمن الذاتي وشرح وأنها بسيطة.

ونعم JSON شيء خانق ولكن في النهاية JSON نفسها مطول. والنفقات العامة من رسم الخرائط لbase64 في هو وسيلة للمشاريع الصغيرة.

وعن طريق رسائل متعددة جزء واحد بشكل صحيح وإما تفكيك الكائن إلى إرسال، استخدام مسار الملكية مثل اسم المعلمة لمزيج التلقائي أو سوف تحتاج إلى إنشاء بروتوكول / شكل آخر للتعبير عن مجرد حمولة.

وتروق أيضا النهج BSON، هذه ليست على نطاق واسع وسهولة بدعم واحدة ترغب أن يكون.

والأساس، ونحن فقط تفوت شيء هنا ولكن تضمين البيانات الثنائية كما تم تأسيس base64 في جيدا وسيلة للذهاب إلا إذا كنت حقا قد حددت الحاجة إلى القيام نقل ثنائي الحقيقي (كما هو الحال في كثير من الأحيان لا يكاد).

ونوع البيانات المخاوف حقا. لقد اختبرت سيناريوهات مختلفة حول ارسال حمولة من مورد مريحة. لترميز ولقد استخدمت باستخدام Base64 (أباتشي) وGZIP ضغط (java.utils.zip. *). وتتضمن حمولة المعلومات حول الفيلم، صورة وملف صوتي. لقد مضغوطة وترميز الصور والملفات الصوتية التي تدهورت بشكل كبير في الأداء. ترميز قبل ضغط تحولت بشكل جيد. وأرسلت صورة ومحتوى الصوت كما بايت المشفرة ومضغوط [].

وراجع: HTTP: // SNIA. غزاله / مواقع / الافتراضي / / ملفات متعددة الأجزاء٪ 20MIME٪ 20Extension٪ 20v1.0g.pdf

ويصف وسيلة لنقل البيانات الثنائية بين العميل والخادم CDMI باستخدام 'CDMI نوع المحتوى "العمليات دون الحاجة إلى تحويل base64 في البيانات الثنائية.

إذا يمكنك استخدام عملية "غير CDMI نوع المحتوى"، وهو مثالي لنقل "بيانات" إلى / من كائن. ويمكن بعد ذلك في وقت لاحق أن تضاف الفوقية / استرجاع إلى / من الكائن ك "CDMI نوع المحتوى" عملية لاحقة.

إذا كنت تستخدم عقدة، وأعتقد أن الطريقة الأكثر فعالية وسهلة لتحويل إلى UTF16 مع:

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

ويمكنك جلب البيانات مرة أخرى من خلال:

Buffer.from(s, 'utf16le');

أحفر أكثر قليلاً (أثناء تنفيذ قاعدة128)، وكشف ذلك عندما نرسل أحرفًا أكبر من 128 رمزًا من رموز ascii، فإن المتصفح (chrome) يرسل في الواقع حرفين (بايت) بدلاً من حرف واحد :(.والسبب هو أن JSON يستخدم بشكل افتراضي أحرف utf8 التي يتم ترميز الأحرف التي تحتوي على رموز ascii أعلى من 127 بواسطة بايتين ما ذكره chmike إجابة.لقد قمت بإجراء الاختبار بهذه الطريقة:اكتب في شريط URL الخاص بالكروم الكروم: // صافي التصدير / ، حدد "تضمين البايتات الأولية"، وابدأ في الالتقاط، وأرسل طلبات POST (باستخدام المقتطف في الأسفل)، وتوقف عن الالتقاط واحفظ ملف json مع بيانات الطلبات الأولية.ثم ننظر داخل ملف json هذا:

  • يمكننا العثور على طلبنا base64 من خلال إيجاد السلسلة 4142434445464748494a4b4c4d4e هذا هو الترميز السداسي لـ ABCDEFGHIJKLMN وسوف نرى ذلك "byte_count": 639 لذلك.
  • يمكننا العثور على طلبنا أعلاه 127 من خلال العثور على السلسلة C2BCC2BDC380C381C382C383C384C385C386C387C388C389C38AC38B هذه هي رموز طلب utf8 السداسية للأحرف ¼½ÀÁÂÃÄÅÆÇÈÉÊË (على الرغم من أن رموز ascii السداسية لهذه الأحرف هي c1c2c3c4c5c6c7c8c9cacbcccdce).ال "byte_count": 703 لذلك فهو أطول بمقدار 64 بايت من طلب base64 لأن الأحرف التي تحتوي على رموز ascii أعلى من 127 يتم ترميزها بمقدار 2 بايت في الطلب :(

لذلك في الحقيقة ليس لدينا ربح من إرسال الأحرف بأكواد >127 :( .بالنسبة لسلاسل base64، لا نلاحظ مثل هذا السلوك السلبي (ربما بالنسبة لـ base85 أيضًا - لا أتحقق من ذلك) - ومع ذلك، قد يكون هناك حل لهذه المشكلة يتمثل في إرسال البيانات في الجزء الثنائي من بيانات POST متعددة الأجزاء/النموذج الموضحة في إجابة أليكس (ولكن عادةً في هذه الحالة لا نحتاج إلى استخدام أي ترميز أساسي على الإطلاق...).

قد يعتمد النهج البديل على تعيين جزء من بيانات بايتين في حرف utf8 صالح واحد عن طريق ترميزه باستخدام شيء مثل Base65280/base65k ولكن ربما سيكون أقل فعالية من base64 بسبب مواصفات utf8 ...

function postBase64() {
  let formData = new FormData();
  let req = new XMLHttpRequest();

  formData.append("base64ch", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
  req.open("POST", '/testBase64ch');
  req.send(formData);
}


function postAbove127() {
  let formData = new FormData();
  let req = new XMLHttpRequest();

  formData.append("above127", "¼½ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüý");
  req.open("POST", '/testAbove127');
  req.send(formData);
}
<button onclick=postBase64()>POST base64 chars</button>
<button onclick=postAbove127()>POST chars with codes>127</button>

وبلدي الحل الآن، XHR2 تستخدم ArrayBuffer. وArrayBuffer كما تسلسل ثنائي يحتوي متعدد الأجزاء المحتوى والفيديو والصوت، والرسوم البيانية، والنص وهلم جرا مع العديد من أنواع المحتوى. الكل في واحد ردا.

في متصفح الحديثة، بعد أن DataView، StringView والنقطة لمكونات مختلفة. انظر أيضا: http://rolfrost.de/video.html للحصول على مزيد من التفاصيل

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top