البحث في الموقع
المحتوى عن 'blob'.
-
يمثل ArrayBuffer جزءًا من معيار "ECMA"، وهو جزء من جافاسكريبت JavaScript، لكن توجد كائنات عالية المستوى ضمن المتصفح وُصِفت في الواجهة البرمجية الخاصة بالملفات File API وبالتحديد الكائن Blob، والذي يتألف من نص افتراضي هو type (من النوع متعدد الوسائط MIME عادةً)، بالإضافة إلى الوسيط blobParts وهو سلسلة من كائنات Blob أخرى ونصوص ومصدر للبيانات الثنائية BufferSource. تأخذ الدالة البانية الصيغة التالية: 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 كعنوان لمورد 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> See the Pen JS-P3-01-Blob-ex1 by Hsoub (@Hsoub) on CodePen. بالإمكان أيضًا إنشاء رابط آليًا في 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); See the Pen JS-P3-01-Blob-ex2 by Hsoub (@Hsoub) on CodePen. يأخذ التابع 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"> حيث سيفكك المتصفح شيفرة البيانات ويعطي كنتيجة الوجه التعبيري التالي: نستخدم الكائن 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(); }; See the Pen JS-P3-01-Blob-ex3 by Hsoub (@Hsoub) on CodePen. يمكن استخدام إحدى الطريقتين السابقتين لتحويل كائن Blob إلى عنوان، لكن تكون عادةً الطريقة (URL.createObjectURL(blob أبسط وأسرع. table { width: 100%; } thead { vertical-align: middle; text-align: center; } td, th { border: 1px solid #dddddd; text-align: right; padding: 8px; text-align: inherit; } tr:nth-child(even) { background-color: #dddddd; } Blob to data url (URL.createObjectURL(blob لا حاجة لإزالة أي شيء لابد من إزالتها إذا كنا نهتم بمقدار الذاكرة المحجوزة خسارة في الأداء والذاكرة لكائنات Blob الضخمة عند الترميز وصول مباشر إلى الكائن Blob، لا حاجة للترميز وفك الترميز تحويل الصورة إلى كائن Blob يمكن تحويل صورة أو جزء من صورة أو لقطة شاشة إلى كائن بيانات ثنائية Blob، وهذا مفيد عند تحميل هذه الصور إلى مكان ما، وتُنفّذ العمليات على الصور باستخدام العنصر <canvas>: ارسم صورةً أو جزءًا منها ضمن لوحة الرسم Canvas باستخدام التابع canvas.drawImage. استدع التابع (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'); See the Pen JS-P3-01-The-clickjacking-attack-ex7 by Hsoub (@Hsoub) on CodePen. يمكن استخدام الصيغة 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. اقرأ أيضًا المقال السابق: ترميز النصوص والتعامل مع كائنات الملفات في جافاسكريبت
-
ماذا لو كانت البيانات الثنائية معلومات نصيةً؟ أي ماذا لو تلقينا ملفًا يحتوي على نص؟ سيسمح لنا الكائن TextDecoder المدمج ضمن JavaScript بقراءة البيانات وتحويلها إلى نص فعلي، بعد تزويده بالمخزن المؤقت buffer الذي يحتوي البيانات الثنائية وطريقة فك الترميز، حيث علينا أوّلًا إنشاء مفكك الترميز Decoder: let decoder = new TextDecoder([label], [options]); حيث: label: يمثل طريقة الترميز، والتي ستكون utf-8 افتراضيًا، كما تدعم كلًا من الطريقتين big5 وwindows-1251 وغيرها. options: وتضم كائنات اختياريةً هي: fatal: قيمة منطقية boolean، حيث عندما تأخذ القيمة "true"، فسترمي استثناءً عند وجود محارف غير صالحة أي لا يمكن فك ترميزها، وإلا -وهي الحالة الافتراضية- فستستبدلها بالمحرف "uFFFD\". ignoreBOM: وهي قيمة منطقية ستتجاهل BOM (وهي علامة Unicode خاصة بترتيب البايتات) التي تُستخدم نادرًا، ويحدث ذلك عندما تأخذ القيمة "true". ومن ثم عملية فك الترميز Decoding: let str = decoder.decode([input], [options]); input: ويمثل كائن BufferSource الذي يحتوي البيانات الثنائية. options: وتضم كائنًا اختياريًا هو: stream: وتأخذ القيمة "true" عندما نريد فك ترميز مجرى تدفق دخل، وذلك عند استدعاء مفكك الترميز باستمرار عن طريق مجموعات البيانات القادمة، وفي هذه الحالة قد يوضع محرف من عدة بايتات multi-byte charecter ليفصل بين هذه المجموعات، ويخبر هذا الخيار مفكك الترميز بتذكر المحارف غير المرمزة، وأن يفك ترميزها عندما تصل المجموعة الجديدة من البيانات. فمثلًا: let uint8Array = new Uint8Array([72, 101, 108, 108, 111]); alert( new TextDecoder().decode(uint8Array) ); // Hello let uint8Array = new Uint8Array([228, 189, 160, 229, 165, 189]); alert( new TextDecoder().decode(uint8Array) ); // 你好 يمكن فك ترميز جزء من المخزن المؤقت بإنشاء مصفوفة ثانوية subarray بالطريقة التالية: let uint8Array = new Uint8Array([0, 72, 101, 108, 108, 111, 0]); // النص في الوسط // أنشئ تمثيلًا جديدًا دون نسخ أي شيء let binaryString = uint8Array.subarray(1, -1); alert( new TextDecoder().decode(binaryString) ); // Hello مرمز النصوص TextEncoder مهمته معاكسة لمهمة مفكك الترميز، إذ يحوّل المُرمِّز TextEncoder النص إلى بايتات. ويُستخدم كالتالي: let encoder = new TextEncoder(); كما يدعم طريقة الترميز "utf-8" فقط. وله تابعان هما: (encode(str: ويعيد كائنًا من النوع Uint8Array انطلاقًا من نص. (encodeInto(str, destination: يُرمز str إلى destination والتي ينبغي أن تكون من النوع Uint8Array. let encoder = new TextEncoder(); let uint8Array = encoder.encode("Hello"); alert(uint8Array); // 72,101,108,108,111 الكائنان File وFileReader يرث الكائن File الكائن Blob ويُوسَّع بإمكانيات تتعلق بنظام الملفات، وتوجد طريقتان لإنشائه: باستخدام الدالة البانية بشكل مشابه للكائن Blob: new File(fileParts, fileName, [options]) حيث: fileparts: يمثل مصفوفةً قد تكون قيمها نصية أو blob أو BufferSource. fileName: اسم الملف. options: وتضم الكائن الاختياري التالي: lastModified: تاريخ آخر تعديل (تاريخ من النوع الصحيح integer). الحصول على ملف باستخدام <"input type="file> أو أسلوب الجر والإفلات، أو غيرها من الواجهات التي يؤمنها المتصفح، حيث يأخذ الملف في هذه الحالات المعلومات السابقة التي استخدمَت بمثابة وسطاء من نظام التشغيل. بما أن الكائن File يرث الكائن Blob، فله خصائصه نفسها، بالإضافة إلى الآتي: name: اسم الملف. lastModified: تاريخ آخر تعديل. إليك مثالًا يصف الحصول على الكائن File باستخدام <"input type="file>: <input type="file" onchange="showFile(this)"> <script> function showFile(input) { let file = input.files[0]; alert(`File name: ${file.name}`); // e.g my.png alert(`Last modified: ${file.lastModified}`); // e.g 1552830408824 } </script> See the Pen JS-P3-02-File-and-FileReader-ex1 by Hsoub (@Hsoub) on CodePen. الكائن FileReader يمثل FileReader كائنًا يخدم غرضًا وحيدًا هو قراءة البيانات من الكائن Blob (وبالتالي من الكائن File أيضًا)، وينقل البيانات مستخدمًا الأحداث، لأن القراءة من القرص قد تستغرق وقتًا، وصيغة الدالة البانية له هي بالشكل: let reader = new FileReader(); // لا وسطاء لهذا الكائن عدة توابع، أهمها: (readAsArrayBuffer(blob: يقرأ البيانات بالصيغة الثنائية على شكل ArrayBuffer. ([readAsText(blob, [encoding: يقرأ البيانات مثل نص مستعمل للتشفير المحدد، وهو utf-8 افتراضيًا. (readAsDataURL(blob: يقرأ البيانات الثنائية ويحولها إلى عنوان بيانات data url مشفر بطريقة base64. ()abort: يلغي العملية. نحدد التابع الذي سيستعمَل في القراءة وفقًا لصيغة البيانات التي نفضلها، وكيف سنستخدمها. readAsArrayBuffer: يُستخدم لقراءة الملفات الثنائية وتنفيذ عمليات ثنائية منخفضة المستوى، بينما نستدعي الكائن File مباشرةً دون قراءة عند تنفيذ عمليات عالية المستوى (ليست ثنائيةً) مثل اقتطاع شرائح من البيانات slicing. readAsText: يُستخدم عند قراءة ملفات نصية والحصول على قيم نصية. readAsDataURL: يُستخدم عندما نريد استخدام البيانات المقروءة في الخاصية src للمعرف img وغيره من المعرّفات، كما توجد طريقة أخرى لقراءة الملف تعرفنا عليها في مقال "كائن البيانات الثنائية Blob"، وهي استخدام التابع (URL.createObjectURL(file يمكن الاستفادة من عدة أحداث تقع أثناء عملية القراءة: loadstart: يقع عند بداية التحميل. progress: يقع خلال القراءة. load: يقع عند انتهاء القراءة دون أخطاء. abort: يقع عند استدعاء التابع ()abort. error: يقع عند حدوث خطأ. loadend: يقع عند انتهاء القراءة بنجاح أو بفشل. يمكن الوصول إلى النتيجة عند انتهاء القراءة بالشكل التالي: reader.result: للحصول على النتيجة عند نجاح العملية. reader.error: للحصول على الخطأ عند إخفاق القراءة. وأكثر الأحداث استخدامًا هما الحدثان load وerror. إليك مثالًا عن قراءة ملف: <input type="file" onchange="readFile(this)"> <script> function readFile(input) { let file = input.files[0]; let reader = new FileReader(); reader.readAsText(file); reader.onload = function() { console.log(reader.result); }; reader.onerror = function() { console.log(reader.error); }; } </script> See the Pen JS-P3-02-File-and-FileReader-ex2 by Hsoub (@Hsoub) on CodePen. خلاصة تعرفنا في هذا المقال على كيفية ترميز النصوص وفك ترميزها بجافاسكريبت، مركزين على مرمز النصوص TextEncoder، بعدها انتقلنا للحديث عن كيفية التعامل مع كائنات الملفات، حيث توصلنا إلى الآتي: يرث الكائن File والكائن Blob. للكائن File الخاصيتين name وlastModified بالإضافة إلى خصائص الكائن Blob وتوابعه، مع إمكانية القراءة من نظام الملفات المرتبط بنظام التشغيل. يمكن الحصول على الكائنات File عن طريق مُدخلات المستخدم مثل <input> أو أحداث الجر والإفلات مثل ondragend. يمكن لكائن FileReader القراءة من ملف أو Blob بإحدى التنسيقات التالية: نص باستخدام التابع readAsText. ArrayBuffer باستخدام التابع readAsArrayBuffer. عنوان بيانات بتشفير base64 باستخدام التابع readAsDataURL. لا نحتاج في بعض الحالات إلى قراءة محتويات ملف، لذا وكما فعلنا مع الكائن blob سننشئ عنوانًا قصيرًا باستخدام الأمر (URL.createObjectURL(file ونسنده إلى المعرف <a> أو <img>، وبهذا يمكن تنزيل الملف أو عرضه كصورة أو كجزء من لوحة رسم canvas. سيكون الأمر بسيطًا إذا كنا سنرسل ملفًا عبر الشبكة، إذ يقبل الكائنان XMLHttpRequest و fetch الكائن File مباشرةً. ترجمة -وبتصرف- للفصلين text decoder and text encoder وFile and FileReader من سلسلة The Modern JavaScript Tutorial. اقرأ أيضًا المقال السابق: مصفوفة المخزن المؤقت ArrayBuffer والمصفوفات الثنائية binary arrays هياكل البيانات: الكائنات والمصفوفات في جافاسكريبت تطويع البيانات في جافاسكربت
-
- كائنات الملفات
- ترميز
-
(و 1 أكثر)
موسوم في: