اذهب إلى المحتوى

التعامل مع كائن البيانات الثنائية Blob في جافاسكربت


ابراهيم الخضور

يمثل ArrayBuffer جزءًا من معيار "ECMA"، وهو جزء من جافاسكريبت JavaScript، لكن توجد كائنات عالية المستوى ضمن المتصفح وُصِفت في الواجهة البرمجية الخاصة بالملفات File API وبالتحديد الكائن Blob، والذي يتألف من نص افتراضي هو type (من النوع متعدد الوسائط MIME عادةً)، بالإضافة إلى الوسيط blobParts وهو سلسلة من كائنات Blob أخرى ونصوص ومصدر للبيانات الثنائية BufferSource.

blob_01.png

تأخذ الدالة البانية الصيغة التالية:

new Blob(blobParts, options);

حيث:

  • blobParts: هو مصفوفة قيمها كائنات Blob وBufferSource وString.
  • options: ويتضمن كائنات اختياريةً هي:
  • type: يمثل نوع الكائن Blob، وهو عادةً من النوع متعدد الوسائط MIME مثل: "image/png".
  • endings: ويحدد إن كنا سنحوّل محرف نهاية السطر للكائن Blob بما يناسب نظام التشغيل الحالي (n\ أو \r\n\)، وسيأخذ افتراضيًا القيمة "transparent" أي لا تفعل شيئًا، وقد يأخذ القيمة "native" أي أَجرِ التحويل.

إليك المثال التالي:

//  (blob) إنشاء كائن بيانات ثنائية من نص
let blob = new Blob(["<html>…</html>"], {type: 'text/html'});
// لاحظ أن الوسيط الأول هو مصفوفة
// إنشاء كائن بيانات ثنائية من نص ومصفوفة
let hello = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" بالصيغة الثنائية

let blob = new Blob([hello, ' ', 'world'], {type: 'text/plain'});

يمكن استخراج الشرائح المكونة للكائن Blob كالتالي:

blob.slice([byteStart], [byteEnd], [contentType]);

حيث:

  • byteStart: بايت البداية وافتراضيًا هو البايت 0.
  • byteEnd: البايت الأخير (ضمنًا، وافتراضيًا حتى آخر المخزن).
  • contentType: نوع كائن blob الجديد، وسيكون افتراضيًا نفس نوع مصدر البيانات.

حيث تشابه هذه الوسائط مقابلاتها في التابع array.slice، ويُسمح باستخدام القيم السالبة.

اقتباس

الكائنات Blobهي كائنات ثابتة

لا يمكن تغيير بيانات Blob مباشرةً، لكن يمكن اجتزاء شرائح منها أو إنشاء كائن Blob جديد أو خلط البيانات في كائن جديد وهكذا، ويشابه هذا السلوك سلوك الكائنات النصية في JavaScript، حيث لا يمكن تغيير حرف في النص، لكن بالإمكان إنشاء نص جديد مصحح.

الكائن Blob كعنوان لمورد URL

يمكن استخدام الكائن Blob مثل عناوين للرابط التشعبي <a> ولمعرّف الصورة <img> لإظهار محتوياتهما بفضل الوسيط type، كما يمكن رفع أو تنزيل الكائنات Blob، وسيتحول النوع type بالطبع إلى Content-Type في طلبات الشبكة network requests.

لنبدأ بالمثال البسيط التالي، فبالنقر على الرابط سينزل كائن Blob مولَّد آليًا يحتوي على النص "Hello world" ضمن ملف:

<!-- download attribute forces the browser to download instead of navigating -->
<a download="hello.txt" href='#' id="link">Download</a>

<script>
let blob = new Blob(["Hello, world!"], {type: 'text/plain'});

link.href = URL.createObjectURL(blob);
</script>

بالإمكان أيضًا إنشاء رابط آليًا في JavaScript، ومن ثم محاكاة عملية النقر بزر الفأرة ()link.click وسيبدأ بعدها التنزيل آليًا، إليك الشيفرة التالية التي تسمح للمستخدم بتنزيل كائن Blob المولَّد آليًا دون أي شيفرة HTML:

let link = document.createElement('a');
link.download = 'hello.txt';

let blob = new Blob(['Hello, world!'], {type: 'text/plain'});

link.href = URL.createObjectURL(blob);

link.click();

URL.revokeObjectURL(link.href);

يأخذ التابع URL.createObjectURL الكائن Blob مثل وسيط وينشئ عنوانًا له على الشكل <blob:<origin>/<uuid.

ستبدو قيمة الخاصية link.href كالتالي:

blob:https://javascript.info/1e67e00e-860d-40a5-89ae-6ab0cbee6273

يُخزِّن المتصفح كل عنوان يولّده التابع URL.createObjectURL على شكل ارتباط map داخلي من الشكل "URL>Blob"، لذا ستكون هذه العناوين قصيرةً، لكنها تسمح بالوصول إلى الكائن Blob، وسيكون العنوان المولَّد -وبالتالي الرابط المتعلق به- صالحًا ضمن المستند الحالي طالما كان مفتوحًا، كما سيسمح بتحديد مرجع للكائن في كل من <img> و<a> وغيرهما من الكائنات التي تحتاج إلى عنوان URL، ومع ذلك هنالك أثر جانبي، فعلى الرغم من ارتباط Blob بالعنوان فهذا الكائن مقيم في الذاكرة، ولا يمكن للمتصفح تحرير الذاكرة المتعلقة به، وسيُزال الارتباط كليًا عند إنهاء المستند، وبالتالي ستتحرر الذاكرة المرتبطة بالكائنات Blob، لكن إن استمر التطبيق لفترة طويلة فلن تحدث هذه العملية خلال فترة وجيزة.

إذا أنشأنا عنوانًا فسيبقى الكائن Blob مقيمًا في الذاكرة حتى لو لم تَعُد هناك حاجة له.

يزيل التابع (URL.revokeObjectURL(url المرجع من علاقة الارتباط الداخلي بين الكائن Blob والعنوان متيحًا المجال لإزالته (إن لم يرتبط بمراجع أخرى) وتحرير الذاكرة، وقد حرصنا في المثال الأخير على استخدام Blob مرةً واحدةً للتنزيل الفوري، لذلك سنستدعي مباشرةً الآتي:

 URL.revokeObjectURL(link.href)

لم نستدع التابع (URL.revokeObjectURL(link.href في مثالنا السابق الذي تضمن رابط HTML قابلًا للنقر، مما سيجعل الكائن Blob غير صالح، ولن يعمل العنوان بعد إلغاء الارتباط المرجعي.

التحويل بين Blob ونص مشفر بطريقة base64

يمكن أن نحوّل الكائن Blob إلى سلسلة نصية بتشفير base64 لتكون يمثابة وسيلة بديلة لاستخدام التابع URL.createObjectURL، يمثل التشفير السابق البيانات الثنائية مثل نص يتكون من محارف ASCII من 0 حتى 64 قابلة للقراءة، ويتمتع بحيز أمان كبير جدًا، والأهم من ذلك إمكانية استخدام هذا التشفير مع عناوين موارد البيانات data urls، والتي لها الشكل:
 

<data:[<mediatype>][;base64],<data

 يمكن استخدام هذه العناوين في أي مكان مثل العناوين النظامية. إليك طريقة تمثيل بيانات تعطي وجهًا تعبيريًا Smiley:

<img src="data:image/png;base64,R0lGODlhDAAMAKIFAF5LAP/zxAAAANyuAP/gaP///wAAAAAAACH5BAEAAAUALAAAAAAMAAwAAAMlWLPcGjDKFYi9lxKBOaGcF35DhWHamZUW0K4mAbiwWtuf0uxFAgA7">

حيث سيفكك المتصفح شيفرة البيانات ويعطي كنتيجة الوجه التعبيري التالي: emoji.png

نستخدم الكائن FileReader المدمج لتحويل Blob إلى base64، حيث يستطيع قراءة البيانات من Blob بصيغ مختلفة، وسنتعمق في هذا الموضوع أكثر في الفصل القادم.

إليك نموذجًا عن تنزيل Blob من خلال التشفير base64 هذه المرة:

let link = document.createElement('a');
link.download = 'hello.txt';

let blob = new Blob(['Hello, world!'], {type: 'text/plain'});

let reader = new FileReader();
reader.readAsDataURL(blob); // استدعاء التحويل

reader.onload = function() {
  link.href = reader.result; // عنوان بيانات
  link.click();
};

يمكن استخدام إحدى الطريقتين السابقتين لتحويل كائن Blob إلى عنوان، لكن تكون عادةً الطريقة (URL.createObjectURL(blob أبسط وأسرع.

Blob to data url (URL.createObjectURL(blob
لا حاجة لإزالة أي شيء لابد من إزالتها إذا كنا نهتم بمقدار الذاكرة المحجوزة
خسارة في الأداء والذاكرة لكائنات Blob الضخمة عند الترميز وصول مباشر إلى الكائن Blob، لا حاجة للترميز وفك الترميز

تحويل الصورة إلى كائن Blob

يمكن تحويل صورة أو جزء من صورة أو لقطة شاشة إلى كائن بيانات ثنائية Blob، وهذا مفيد عند تحميل هذه الصور إلى مكان ما، وتُنفّذ العمليات على الصور باستخدام العنصر <canvas>:

  1. ارسم صورةً أو جزءًا منها ضمن لوحة الرسم Canvas باستخدام التابع canvas.drawImage.
  2. استدع التابع (toBlob(callback, format, quality الذي يُنشئ وينفّذ استدعاءً محددًا عندما يكتمل.

ستجد في المثال التالي صورةً نُسخت للتو، لكن يمكن اقتطاع جزء منها أو نقلها إلى لوحة رسم قبل إنشاء Blob:

// خذ أية صورة
let img = document.querySelector('img');

// شكّل لوحة رسم بنفس الحجم
let canvas = document.createElement('canvas');
canvas.width = img.clientWidth;
canvas.height = img.clientHeight;

let context = canvas.getContext('2d');

// انسخ الصورة إلى اللوحة
context.drawImage(img, 0, 0);
// يمكن أن نعدل اللوحة كما نشاء

// عملية التحويل إلى كائن ثنائي غير متزامنة
canvas.toBlob(function(blob) {
  //الكائن جاهز سننزله
  let link = document.createElement('a');
  link.download = 'example.png';

  link.href = URL.createObjectURL(blob);
  link.click();

  // امسح المرجع الداخلي للكائن حتى يتمكن Canvas المتصفح من إزالته 
  URL.revokeObjectURL(link.href);
}, 'image/png');

يمكن استخدام الصيغة async/await بدلًا من دوال الاستدعاء:

let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));

يمكن استخدام مكتبات خاصة لالتقاط صورة للشاشة، وما عليك إلا الانتقال ضمن الصفحة ورسمها ضمن لوحة <canvas>، ثم يمكن نقلها بعد ذلك إلى Blob بنفس الأسلوب السابق.

التحويل من الكائن Blob إلى الكائن arrayBuffer

تسمح الدالة البانية للكائن Blob بإنشاء هذا الكائن من أي شيء تقريبًا، بما في ذلك أية كائنات BufferSource، لكن لو أردنا إنجاز عملية معالجة منخفضة المستوى، فيمكننا الحصول على كائن ArrayBuffer ذو مستوى أدنى مستخدمين FileReader:

// get arrayBuffer from blob
let fileReader = new FileReader();

fileReader.readAsArrayBuffer(blob);

fileReader.onload = function(event) {
  let arrayBuffer = fileReader.result;
};

خلاصة

  • تمثل الكائنات ArrayBuffer وUint8Array وغيرها من الكائنات التي تنضوي تحت المصطلح BufferSource بيانات ثنائيةً، بينما يمثل Blob بيانات ثنائيةً لها نوع، مما يجعل الكائن Blob مناسبًا لعمليات الرفع والتنزيل التي تستخدم بكثرة من المتصفح.
  • يمكن للتوابع التي تُنفِّذ طلبات الويب web-requests مثل: XMLHttpRequest وfetch وغيرها؛ أن تعمل مع Blob كما تعمل مع غيره من أنواع البيانات الثنائية.
  • يمكن التحويل بسهولة بين Blob وكائنات البيانات الثنائية منخفضة المستوى:
  • يمكن التحويل بين Blob ومصفوفة النوع باستخدام الدالة البانية (...)new Blob
  • يمكن الحصول على الكائن ArrayBuffer من الكائن Blob باستخدام التعليمة FileReader، ثم إنشاء كائن استعراض بناءً على هذا الأخير، وذلك لمعالجة البيانات الثنائية في مستويات عمل منخفضة.

ترجمة -وبتصرف- للفصل Blob من سلسلة The Modern JavaScript Tutorial.

اقرأ أيضًا


تفاعل الأعضاء

أفضل التعليقات

لا توجد أية تعليقات بعد



انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أضف تعليق

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.


×
×
  • أضف...