ফেচ এপিআই দিয়ে স্ট্রিমিং রিকোয়েস্ট,ফেচ এপিআই দিয়ে স্ট্রিমিং রিকোয়েস্ট

Chromium 105 থেকে, আপনি স্ট্রীমস API ব্যবহার করে পুরো বডি উপলব্ধ হওয়ার আগে একটি অনুরোধ শুরু করতে পারেন৷

আপনি এটি ব্যবহার করতে পারেন:

  • সার্ভার গরম করুন। অন্য কথায়, ব্যবহারকারী একটি টেক্সট ইনপুট ক্ষেত্রে ফোকাস করার পরে আপনি অনুরোধটি শুরু করতে পারেন এবং সমস্ত শিরোনামগুলিকে পথের বাইরে নিয়ে যান, তারপর ব্যবহারকারী তাদের প্রবেশ করা ডেটা পাঠানোর আগে 'পাঠান' টিপে পর্যন্ত অপেক্ষা করুন।
  • ধীরে ধীরে ক্লায়েন্টে তৈরি করা ডেটা পাঠান, যেমন অডিও, ভিডিও বা ইনপুট ডেটা।
  • HTTP/2 বা HTTP/3 এর মাধ্যমে ওয়েব সকেট পুনরায় তৈরি করুন।

কিন্তু যেহেতু এটি একটি নিম্ন-স্তরের ওয়েব প্ল্যাটফর্ম বৈশিষ্ট্য, তাই আমার ধারণা দ্বারা সীমাবদ্ধ থাকবেন না। হয়তো আপনি অনুরোধ স্ট্রিমিংয়ের জন্য আরও উত্তেজনাপূর্ণ ব্যবহার-কেস সম্পর্কে ভাবতে পারেন।

ডেমো

এটি দেখায় কিভাবে আপনি ব্যবহারকারী থেকে সার্ভারে ডেটা স্ট্রিম করতে পারেন এবং ডেটা ফেরত পাঠাতে পারেন যা রিয়েল টাইমে প্রক্রিয়া করা যেতে পারে।

হ্যাঁ ঠিক আছে এটা সবচেয়ে কল্পনাপ্রসূত উদাহরণ নয়, আমি শুধু এটা সহজ রাখতে চেয়েছিলাম, ঠিক আছে?

যাইহোক, কিভাবে এই কাজ করে?

আগে ফেচ স্ট্রীম এর উত্তেজনাপূর্ণ অ্যাডভেঞ্চারে

প্রতিক্রিয়া স্ট্রীম এখন কিছু সময়ের জন্য সমস্ত আধুনিক ব্রাউজারে উপলব্ধ। সার্ভার থেকে আসার সাথে সাথে তারা আপনাকে প্রতিক্রিয়ার অংশগুলি অ্যাক্সেস করার অনুমতি দেয়:

const response = await fetch(url);
const reader = response.body.getReader();

while (true) {
  const {value, done} = await reader.read();
  if (done) break;
  console.log('Received', value);
}

console.log('Response fully received');

প্রতিটি value হল বাইটের Uint8Array . আপনি যে অ্যারের সংখ্যা পাবেন এবং অ্যারের আকার নেটওয়ার্কের গতির উপর নির্ভর করে। আপনি যদি দ্রুত সংযোগে থাকেন তবে আপনি কম, বড় 'খণ্ড' ডেটা পাবেন। আপনি একটি ধীর সংযোগে থাকলে, আপনি আরও, ছোট অংশ পাবেন।

আপনি যদি বাইটগুলিকে টেক্সটে রূপান্তর করতে চান তবে আপনি TextDecoder ব্যবহার করতে পারেন, অথবা যদি আপনার টার্গেট ব্রাউজারগুলি এটি সমর্থন করে তবে নতুন রূপান্তর স্ট্রীম ব্যবহার করতে পারেন:

const response = await fetch(url);
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();

TextDecoderStream হল একটি ট্রান্সফর্ম স্ট্রীম যা সেই সমস্ত Uint8Array খণ্ডগুলিকে ধরে এবং স্ট্রিংগুলিতে রূপান্তর করে।

স্ট্রিমগুলি দুর্দান্ত, কারণ আপনি ডেটা আসার সাথে সাথে কাজ শুরু করতে পারেন৷ উদাহরণস্বরূপ, আপনি যদি 100টি 'ফলাফল'-এর একটি তালিকা পেয়ে থাকেন, তাহলে আপনি 100টির জন্য অপেক্ষা না করে এটি পাওয়ার সাথে সাথে প্রথম ফলাফল প্রদর্শন করতে পারেন।

যাইহোক, এটি প্রতিক্রিয়া স্ট্রীম, আমি যে উত্তেজনাপূর্ণ নতুন জিনিস সম্পর্কে কথা বলতে চেয়েছিলাম তা হল অনুরোধ স্ট্রীম।

স্ট্রিমিং অনুরোধ সংস্থা

অনুরোধের দেহ থাকতে পারে:

await fetch(url, {
  method: 'POST',
  body: requestBody,
});

পূর্বে, আপনি অনুরোধ শুরু করার আগে আপনার পুরো শরীরকে প্রস্তুত করার প্রয়োজন ছিল, কিন্তু এখন Chromium 105-এ, আপনি আপনার নিজস্ব ReadableStream ডেটা সরবরাহ করতে পারেন:

function wait(milliseconds) {
  return new Promise(resolve => setTimeout(resolve, milliseconds));
}

const stream = new ReadableStream({
  async start(controller) {
    await wait(1000);
    controller.enqueue('This ');
    await wait(1000);
    controller.enqueue('is ');
    await wait(1000);
    controller.enqueue('a ');
    await wait(1000);
    controller.enqueue('slow ');
    await wait(1000);
    controller.enqueue('request.');
    controller.close();
  },
}).pipeThrough(new TextEncoderStream());

fetch(url, {
  method: 'POST',
  headers: {'Content-Type': 'text/plain'},
  body: stream,
  duplex: 'half',
});

উপরেরটি সার্ভারে "এটি একটি ধীর অনুরোধ" পাঠাবে, এক সময়ে একটি শব্দ, প্রতিটি শব্দের মধ্যে এক সেকেন্ড বিরতি সহ।

অনুরোধের বডির প্রতিটি অংশকে বাইটের একটি Uint8Array হতে হবে, তাই আমি আমার জন্য রূপান্তর করতে pipeThrough(new TextEncoderStream()) ব্যবহার করছি।

বিধিনিষেধ

স্ট্রিমিং অনুরোধগুলি ওয়েবের জন্য একটি নতুন শক্তি, তাই তারা কয়েকটি বিধিনিষেধ নিয়ে আসে:

হাফ ডুপ্লেক্স?

একটি অনুরোধে স্ট্রিমগুলি ব্যবহার করার অনুমতি দেওয়ার জন্য, duplex অনুরোধ বিকল্পটিকে 'half' এ সেট করতে হবে।

HTTP-এর একটি স্বল্প-পরিচিত বৈশিষ্ট্য (যদিও, এটি আদর্শ আচরণ কিনা তা নির্ভর করে আপনি কাকে জিজ্ঞাসা করছেন) তা হল আপনি যখন অনুরোধটি পাঠাচ্ছেন তখনও আপনি প্রতিক্রিয়া পেতে শুরু করতে পারেন। যাইহোক, এটি এত কম পরিচিত যে এটি সার্ভার দ্বারা সমর্থিত নয়, এবং কোন ব্রাউজার দ্বারা সমর্থিত নয়৷

ব্রাউজারগুলিতে, অনুরোধের অংশটি সম্পূর্ণরূপে পাঠানো না হওয়া পর্যন্ত প্রতিক্রিয়া কখনই উপলব্ধ হয় না, এমনকি সার্ভার তাড়াতাড়ি একটি প্রতিক্রিয়া পাঠালেও। এটি সমস্ত ব্রাউজার আনার জন্য সত্য।

এই ডিফল্ট প্যাটার্নটি 'হাফ ডুপ্লেক্স' নামে পরিচিত। যাইহোক, কিছু ইমপ্লিমেন্টেশন, যেমন Deno-এ fetch , স্ট্রিমিং ফেচের জন্য 'ফুল ডুপ্লেক্স'-এ ডিফল্ট করা হয়েছে, যার অর্থ অনুরোধ সম্পূর্ণ হওয়ার আগেই প্রতিক্রিয়া পাওয়া যেতে পারে।

সুতরাং, এই সামঞ্জস্যের সমস্যাটি সমাধান করার জন্য, ব্রাউজারগুলিতে, duplex: 'half' নির্দিষ্ট করা দরকার।

ভবিষ্যতে, duplex: 'full' স্ট্রিমিং এবং নন-স্ট্রিমিং অনুরোধের জন্য ব্রাউজারে সমর্থিত হতে পারে।

ইতিমধ্যে, ডুপ্লেক্স যোগাযোগের পরবর্তী সর্বোত্তম জিনিসটি হল একটি স্ট্রিমিং অনুরোধের সাথে একটি আনয়ন করা, তারপর স্ট্রিমিং প্রতিক্রিয়া পাওয়ার জন্য অন্যটি আনা। এই দুটি অনুরোধ সংযুক্ত করার জন্য সার্ভারের কিছু উপায় প্রয়োজন, যেমন URL-এ একটি আইডি। এভাবেই ডেমো কাজ করে।

সীমাবদ্ধ পুনঃনির্দেশ

HTTP রিডাইরেক্টের কিছু ফর্মের জন্য ব্রাউজারকে অনুরোধের মূল অংশটি অন্য URL-এ পাঠাতে হবে। এটি সমর্থন করার জন্য, ব্রাউজারটিকে স্ট্রিমের বিষয়বস্তুগুলিকে বাফার করতে হবে, যা বিন্দুটিকে পরাজিত করে, তাই এটি তা করে না।

পরিবর্তে, যদি অনুরোধের একটি স্ট্রিমিং বডি থাকে, এবং প্রতিক্রিয়াটি 303 ব্যতীত একটি HTTP পুনঃনির্দেশ হয়, তবে আনা প্রত্যাখ্যান হবে এবং পুনঃনির্দেশ অনুসরণ করা হবে না

303টি পুনঃনির্দেশ অনুমোদিত, যেহেতু তারা স্পষ্টভাবে পদ্ধতিটিকে GET এ পরিবর্তন করে এবং অনুরোধের মূল অংশটি বাতিল করে।

CORS প্রয়োজন এবং একটি প্রিফ্লাইট ট্রিগার করে

স্ট্রিমিং অনুরোধের একটি প্রধান অংশ আছে, কিন্তু একটি Content-Length শিরোনাম নেই। এটি একটি নতুন ধরনের অনুরোধ, তাই CORS প্রয়োজন, এবং এই অনুরোধগুলি সর্বদা একটি প্রিফ্লাইট ট্রিগার করে৷

স্ট্রিমিং no-cors অনুরোধ অনুমোদিত নয়।

HTTP/1.x এ কাজ করে না

সংযোগ HTTP/1.x হলে আনয়ন প্রত্যাখ্যান করা হবে।

এর কারণ হল, HTTP/1.1 নিয়ম অনুসারে, অনুরোধ এবং প্রতিক্রিয়া সংস্থাগুলিকে একটি Content-Length শিরোনাম পাঠাতে হবে, তাই অন্য পক্ষ জানে যে এটি কত ডেটা পাবে, বা খণ্ডিত এনকোডিং ব্যবহার করতে বার্তাটির বিন্যাস পরিবর্তন করতে হবে। খণ্ডিত এনকোডিংয়ের সাহায্যে, দেহটি অংশে বিভক্ত হয়, প্রতিটির নিজস্ব বিষয়বস্তুর দৈর্ঘ্য থাকে।

এইচটিটিপি/1.1 প্রতিক্রিয়াগুলির ক্ষেত্রে খণ্ডিত এনকোডিং বেশ সাধারণ, কিন্তু অনুরোধের ক্ষেত্রে এটি খুব বিরল, তাই এটি একটি সামঞ্জস্যের ঝুঁকির অনেক বেশি৷

সম্ভাব্য সমস্যা

এটি একটি নতুন বৈশিষ্ট্য, এবং এটি আজ ইন্টারনেটে কম ব্যবহার করা হয়৷ এখানে দেখার জন্য কিছু সমস্যা রয়েছে:

সার্ভারের দিকে অসঙ্গতি

কিছু অ্যাপ সার্ভার স্ট্রিমিং অনুরোধ সমর্থন করে না, এবং পরিবর্তে আপনাকে এটির কোনটি দেখতে দেওয়ার আগে সম্পূর্ণ অনুরোধ প্রাপ্ত হওয়ার জন্য অপেক্ষা করুন, যা বিন্দুটিকে পরাজিত করে। পরিবর্তে, স্ট্রিমিং সমর্থন করে এমন একটি অ্যাপ সার্ভার ব্যবহার করুন, যেমন NodeJS বা Deno

কিন্তু, আপনি এখনও বনের বাইরে নন! অ্যাপ্লিকেশন সার্ভার, যেমন নোডজেএস, সাধারণত অন্য সার্ভারের পিছনে বসে থাকে, যাকে প্রায়শই "ফ্রন্ট-এন্ড সার্ভার" বলা হয়, যা CDN এর পিছনে বসে থাকতে পারে। যদি তাদের মধ্যে কেউ অনুরোধটি চেইনের পরবর্তী সার্ভারে দেওয়ার আগে বাফার করার সিদ্ধান্ত নেয়, আপনি অনুরোধ স্ট্রিমিংয়ের সুবিধা হারাবেন।

আপনার নিয়ন্ত্রণের বাইরে অসঙ্গতি

যেহেতু এই বৈশিষ্ট্যটি শুধুমাত্র HTTPS-এ কাজ করে, তাই আপনাকে আপনার এবং ব্যবহারকারীর মধ্যে প্রক্সি নিয়ে চিন্তা করার দরকার নেই, তবে ব্যবহারকারী তাদের মেশিনে একটি প্রক্সি চালাচ্ছেন। কিছু ইন্টারনেট সুরক্ষা সফ্টওয়্যার এটি ব্রাউজার এবং নেটওয়ার্কের মধ্যে যা কিছু যায় তা নিরীক্ষণ করার অনুমতি দেওয়ার জন্য এটি করে এবং এমন কিছু ক্ষেত্রেও হতে পারে যেখানে এই সফ্টওয়্যারটি সংস্থাগুলিকে অনুরোধ করে৷

আপনি যদি এর বিরুদ্ধে রক্ষা করতে চান, আপনি উপরের ডেমোর মতো একটি 'ফিচার টেস্ট' তৈরি করতে পারেন, যেখানে আপনি স্ট্রিম বন্ধ না করে কিছু ডেটা স্ট্রিম করার চেষ্টা করেন। সার্ভার ডেটা গ্রহণ করলে, এটি একটি ভিন্ন আনার মাধ্যমে প্রতিক্রিয়া জানাতে পারে। একবার এটি হয়ে গেলে, আপনি জানেন যে ক্লায়েন্ট এন্ড-টু-এন্ড স্ট্রিমিং অনুরোধ সমর্থন করে।

বৈশিষ্ট্য সনাক্তকরণ

const supportsRequestStreams = (() => {
  let duplexAccessed = false;

  const hasContentType = new Request('', {
    body: new ReadableStream(),
    method: 'POST',
    get duplex() {
      duplexAccessed = true;
      return 'half';
    },
  }).headers.has('Content-Type');

  return duplexAccessed && !hasContentType;
})();

if (supportsRequestStreams) {
  // …
} else {
  // …
}

আপনি যদি কৌতূহলী হন তবে বৈশিষ্ট্য সনাক্তকরণ কীভাবে কাজ করে তা এখানে:

যদি ব্রাউজারটি একটি নির্দিষ্ট body টাইপ সমর্থন না করে, তাহলে এটি বস্তুটিতে toString() কল করে এবং ফলাফলটিকে বডি হিসাবে ব্যবহার করে। সুতরাং, যদি ব্রাউজার অনুরোধ স্ট্রীম সমর্থন না করে, অনুরোধের অংশটি "[object ReadableStream]" স্ট্রিং হয়ে যায়। যখন একটি স্ট্রিং একটি বডি হিসাবে ব্যবহার করা হয়, তখন এটি সুবিধাজনকভাবে Content-Type শিরোনামটিকে text/plain;charset=UTF-8 এ সেট করে। সুতরাং, যদি সেই শিরোনামটি সেট করা থাকে, তাহলে আমরা জানি যে ব্রাউজার অনুরোধ বস্তুগুলিতে স্ট্রীম সমর্থন করে না এবং আমরা তাড়াতাড়ি প্রস্থান করতে পারি।

সাফারি অনুরোধ অবজেক্টে স্ট্রীম সমর্থন করে , কিন্তু তাদের fetch এর সাথে ব্যবহার করার অনুমতি দেয় না , তাই duplex বিকল্পটি পরীক্ষা করা হয়, যা Safari বর্তমানে সমর্থন করে না।

লিখনযোগ্য স্ট্রীম ব্যবহার করে

কখনও কখনও আপনার কাছে WritableStream থাকলে স্ট্রিমগুলির সাথে কাজ করা সহজ হয়৷ আপনি একটি 'পরিচয়' স্ট্রীম ব্যবহার করে এটি করতে পারেন, এটি একটি পঠনযোগ্য/লেখাযোগ্য জুটি যা লেখার যোগ্য প্রান্তে পাস করা যেকোনো কিছু নিয়ে যায় এবং এটি পাঠযোগ্য প্রান্তে পাঠায়। আপনি কোনও যুক্তি ছাড়াই একটি TransformStream তৈরি করে এর মধ্যে একটি তৈরি করতে পারেন:

const {readable, writable} = new TransformStream();

const responsePromise = fetch(url, {
  method: 'POST',
  body: readable,
});

এখন, আপনি লেখার যোগ্য স্ট্রীমে যা পাঠাবেন তা অনুরোধের অংশ হবে। এটি আপনাকে একসাথে স্ট্রীম রচনা করতে দেয়। উদাহরণস্বরূপ, এখানে একটি নির্বোধ উদাহরণ যেখানে একটি URL থেকে ডেটা আনা হয়, সংকুচিত করা হয় এবং অন্য URL-এ পাঠানো হয়:

// Get from url1:
const response = await fetch(url1);
const {readable, writable} = new TransformStream();

// Compress the data from url1:
response.body.pipeThrough(new CompressionStream('gzip')).pipeTo(writable);

// Post to url2:
await fetch(url2, {
  method: 'POST',
  body: readable,
});

উপরের উদাহরণটি জিজিপ ব্যবহার করে নির্বিচারে ডেটা সংকুচিত করতে কম্প্রেশন স্ট্রীম ব্যবহার করে।