جافا سكريبت - الفرق بين Web Streams و Node.js Stream APIs


أنا أبني تطبيق رفع ملفات لأتعرف على مفهوم التدفقات. أحاول تحويل قائمة ملفات أو كائنات Blob إلى تدفق ثم رفعها إلى الخادم وتخزينها على نظام الملفات.

لم أواجه أي مشكلة مع تنفيذات الواجهة الأمامية والخلفية على التوالي، لكنني أجد صعوبة في ربط الاثنين. مشكلتي الرئيسية هي أنني لا أفهم الفرق بين واجهة برمجة تطبيقات Web Streams وواجهة برمجة تطبيقات Node.js Streams. تمكنت من تحويل كتل الملفات المحددة في المتصفح إلى Web ReadableStream، لكن الحزم التي جربتها (axios للطلبات، وsocket.io وsocket.io-stream للويب سوكيت) تقبل فقط نسخة Node.js من Stream كوسائط. كما أنني لم أتمكن من تمرير Web ReadableStream إلى Node.js Writeable أو Duplex Stream. أسماء الأساليب أيضًا مختلفة (على سبيل المثال: pipeTo أو pipeThrough في واجهة برمجة تطبيقات الويب وpipe في واجهة برمجة تطبيقات Node.js).

أعلم أن هناك اختلافات في التنفيذ بين Node.js والمتصفحات، ولكن بسذاجة، كنت أظن أن واجهات البرمجة ستكون مشابهة. هل يمكنني بطريقة بسيطة تحويل بين تدفقات الويب وتدفقات Node.js المحوّلة للمتصفح وأنا أفتقد شيئًا ما؟ هل يستحق استخدام واجهة تدفق الويب على stream-browserify؟

الإجابة رقم 1

ليس من الصعب جدًا تحويل تدفق ويب إلى تدفق Node.js يدويًا، لكن يجب عليك حقًا محاولة العثور على مكتبة تقبل تدفقات الويب الأصلية بدلاً من إجبار محاكي Node.js لـ stream المدمج للعمل في المتصفح باستخدام Browserify.

ومع ذلك، إذا ثبت أنه من الضروري استخدام تزييف تدفق Node.js في المتصفح، تحتاج إلى تثبيت stream-browserify واستخدامه على هذا النحو:

import { Readable, Writable } from 'stream-browserify;'

// window.ReadableStream to Node.js Readable
const webRSToNodeRS = rs => {
  const reader = rs.getReader();
  const out = new Readable();
  reader.read().then(async ({ value, done }) => {
    while (!done) {
      out.push(value);
      ({ done, value } = await reader.read());
    }
    out.push(null);
  });
  return out;
}

// window.WritableStream to Node.js Writable
const webWSToNodeWS = ws => {
  const writer = ws.getWriter();
  const out = new Writable();
  out._write = (chunk, encoding, callback) => {
    writer.write(chunk);
    callback();
  };
  out._final = callback => {
    writer.close();
    callback();
  };
  return out;
}

ينبغي أن تكون هذه الطرق كافية لتحقيق التفاعل الكامل بين تدفقات الويب و Node. على سبيل المثال، إذا كنت تريد توصيل تدفق ReadableStream من الويب إلى Writable/Duplex في Node.js:

const pipeWebRSToWritable = (rs, writable) => {
  // After converting you can use the normal pipe methods
  webRSToNodeRS(rs).pipe(writable);
}

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

// POST a ReadableStream (perhaps of a file) to the server
// Way easier and more performant than using a 3rd party library...
const postRSToServer = rs => fetch('/your/server/endpoint', {
  method: 'POST',
  body: rs
});

ملاحظة أخيرة: تأكد من أنك تستخدم مباشرة طريقة Blob.prototype.stream (استدعي هذا على كائن File، على سبيل المثال file.stream()، لأن File يمتد من Blob). هناك بعض الطرق للحصول على ReadableStream من ملف في JS والتي تنتهي فعليًا بتحميل كل الملف في الذاكرة في المتصفح، وهو ما لا تريده.

الإجابة رقم 2

حسب الوثائق، منذ الإصدار 16، يتضمن Node.js تنفيذًا (لا يزال تجريبيًا) لواجهة برمجة تطبيقات WebStreams.

وقد حصل واجهة برمجة التطبيقات القديمة للتدفقات الآن (منذ الإصدار 17) على بعض طرق toWeb/FromWeb للجسر بين الاثنين.

بعض المعلومات هنا.