XMLHttpRequest2 এ নতুন কৌশল

ভূমিকা

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

দেখা যাচ্ছে আমাদের পুরানো বন্ধুটি একটি বিশাল মেকওভার পেয়েছে কিন্তু অনেক লোক এর নতুন বৈশিষ্ট্য সম্পর্কে অবগত নয়। XMLHttpRequest লেভেল 2 অনেকগুলি নতুন ক্ষমতার পরিচয় দেয় যা আমাদের ওয়েব অ্যাপগুলিতে জটিল হ্যাকগুলির অবসান ঘটায়; ক্রস-অরিজিন অনুরোধ, অগ্রগতি ইভেন্ট আপলোড করা এবং বাইনারি ডেটা আপলোড/ডাউনলোড করার জন্য সমর্থনের মতো জিনিস। এগুলো AJAX কে অনেক ব্লিডিং এজ HTML5 এপিআই যেমন ফাইল সিস্টেম এপিআই , ওয়েব অডিও এপিআই এবং ওয়েবজিএল এর সাথে একত্রে কাজ করার অনুমতি দেয়।

এই টিউটোরিয়ালটি XMLHttpRequest এর কিছু নতুন বৈশিষ্ট্য তুলে ধরেছে, বিশেষ করে যেগুলি ফাইলের সাথে কাজ করার জন্য ব্যবহার করা যেতে পারে।

তথ্য আনয়ন

বাইনারি ব্লব হিসাবে একটি ফাইল আনা XHR এর সাথে বেদনাদায়ক ছিল। প্রযুক্তিগতভাবে, এটি এমনকি সম্ভব ছিল না. একটি কৌশল যা ভালভাবে নথিভুক্ত করা হয়েছে তা হল নিচের মত ব্যবহারকারী-সংজ্ঞায়িত অক্ষরসেট দিয়ে মাইম টাইপ ওভাররাইড করা।

একটি ছবি আনার পুরানো উপায়:

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);

// Hack to pass bytes through unprocessed.
xhr.overrideMimeType('text/plain; charset=x-user-defined');

xhr.onreadystatechange = function(e) {
  if (this.readyState == 4 && this.status == 200) {
    var binStr = this.responseText;
    for (var i = 0, len = binStr.length; i < len; ++i) {
      var c = binStr.charCodeAt(i);
      //String.fromCharCode(c & 0xff);
      var byte = c & 0xff;  // byte at offset i
    }
  }
};

xhr.send();

এটি কাজ করার সময়, আপনি আসলে responseText যা পাবেন তা বাইনারি ব্লব নয়। এটি একটি বাইনারি স্ট্রিং যা ইমেজ ফাইলের প্রতিনিধিত্ব করে। আমরা ডেটা ফেরত দেওয়ার জন্য সার্ভারকে প্রতারণা করছি, প্রক্রিয়াবিহীন। যদিও এই ছোট্ট রত্নটি কাজ করে, আমি এটিকে কালো জাদু বলব এবং এর বিরুদ্ধে পরামর্শ দেব। যেকোন সময় আপনি ক্যারেক্টার কোড হ্যাক এবং স্ট্রিং ম্যানিপুলেশন অবলম্বন করে ডেটাকে একটি পছন্দসই বিন্যাসে বাধ্য করার জন্য, এটি একটি সমস্যা।

একটি প্রতিক্রিয়া বিন্যাস উল্লেখ

পূর্ববর্তী উদাহরণে, আমরা সার্ভারের মাইম টাইপ ওভাররাইড করে এবং বাইনারি স্ট্রিং হিসাবে প্রতিক্রিয়া পাঠ্য প্রক্রিয়াকরণ করে একটি বাইনারি "ফাইল" হিসাবে ছবিটি ডাউনলোড করেছি। পরিবর্তে, ব্রাউজারকে জানানোর জন্য XMLHttpRequest এর নতুন responseType এবং response প্রপার্টিগুলিকে আমরা কী ফর্ম্যাটে ডেটা ফেরত দিতে চাই তা জানাই।

xhr প্রতিক্রিয়া প্রকার
একটি অনুরোধ পাঠানোর আগে, আপনার ডেটা প্রয়োজনের উপর নির্ভর করে xhr.responseType কে "টেক্সট", "অ্যারেবাফার", "ব্লব" বা "ডকুমেন্ট" এ সেট করুন। দ্রষ্টব্য, xhr.responseType = '' সেট করা (বা বাদ দেওয়া) "টেক্সট" এর প্রতিক্রিয়া ডিফল্ট করবে।
xhr প্রতিক্রিয়া
একটি সফল অনুরোধের পরে, xhr এর প্রতিক্রিয়া সম্পত্তিতে অনুরোধ করা ডেটা একটি DOMString , ArrayBuffer , Blob , বা Document হিসাবে থাকবে ( responseType এর জন্য কী সেট করা হয়েছিল তার উপর নির্ভর করে।)

এই নতুন দুর্দান্ততার সাথে, আমরা আগের উদাহরণটি পুনরায় কাজ করতে পারি, কিন্তু এইবার, একটি স্ট্রিংয়ের পরিবর্তে একটি Blob হিসাবে চিত্রটি আনুন:

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'blob';

xhr.onload = function(e) {
  if (this.status == 200) {
    // Note: .response instead of .responseText
    var blob = new Blob([this.response], {type: 'image/png'});
    ...
  }
};

xhr.send();

অধিক সুন্দর!

ArrayBuffer প্রতিক্রিয়া

একটি ArrayBuffer বাইনারি ডেটার জন্য একটি সাধারণ নির্দিষ্ট দৈর্ঘ্যের ধারক। আপনার যদি কাঁচা ডেটার একটি সাধারণ বাফারের প্রয়োজন হয় তবে এগুলি খুব সহজ, তবে এই লোকগুলির পিছনে আসল শক্তি হল আপনি জাভাস্ক্রিপ্ট টাইপ করা অ্যারে ব্যবহার করে অন্তর্নিহিত ডেটার "ভিউ" তৈরি করতে পারেন। আসলে, একটি একক ArrayBuffer উৎস থেকে একাধিক ভিউ তৈরি করা যেতে পারে। উদাহরণস্বরূপ, আপনি একটি 8-বিট পূর্ণসংখ্যা অ্যারে তৈরি করতে পারেন যা একই ডেটা থেকে বিদ্যমান 32-বিট পূর্ণসংখ্যা অ্যারে হিসাবে একই ArrayBuffer ভাগ করে। অন্তর্নিহিত ডেটা একই থাকে, আমরা কেবল এটির বিভিন্ন উপস্থাপনা তৈরি করি।

একটি উদাহরণ হিসাবে, নিম্নলিখিতটি একটি ArrayBuffer হিসাবে আমাদের একই চিত্রটি নিয়ে আসে, কিন্তু এইবার, সেই ডেটা বাফার থেকে একটি স্বাক্ষরবিহীন 8-বিট পূর্ণসংখ্যা অ্যারে তৈরি করে:

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'arraybuffer';

xhr.onload = function(e) {
  var uInt8Array = new Uint8Array(this.response); // this.response == uInt8Array.buffer
  // var byte3 = uInt8Array[4]; // byte at offset 4
  ...
};

xhr.send();

ব্লব প্রতিক্রিয়া

আপনি যদি সরাসরি একটি Blob সাথে কাজ করতে চান এবং/অথবা ফাইলের কোনো বাইট ব্যবহার করতে না চান, তাহলে xhr.responseType='blob' ব্যবহার করুন :

window.URL = window.URL || window.webkitURL;  // Take care of vendor prefixes.

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'blob';

xhr.onload = function(e) {
  if (this.status == 200) {
    var blob = this.response;

    var img = document.createElement('img');
    img.onload = function(e) {
      window.URL.revokeObjectURL(img.src); // Clean up after yourself.
    };
    img.src = window.URL.createObjectURL(blob);
    document.body.appendChild(img);
    ...
  }
};

xhr.send();

একটি Blob অনেক জায়গায় ব্যবহার করা যেতে পারে, যার মধ্যে এটিকে ইনডেক্সডডিবিতে সংরক্ষণ করা, এটিকে HTML5 ফাইল সিস্টেমে লেখা, বা একটি ব্লব URL তৈরি করা , যেমনটি এই উদাহরণে দেখা গেছে।

ডেটা পাঠানো হচ্ছে

বিভিন্ন ফরম্যাটে ডেটা ডাউনলোড করতে পারা দুর্দান্ত, কিন্তু আমরা যদি এই সমৃদ্ধ ফর্ম্যাটগুলিকে হোম বেস (সার্ভারে) ফেরত পাঠাতে না পারি তবে এটি আমাদের কোথাও পাবে না। XMLHttpRequest আমাদের কিছু সময়ের জন্য DOMString বা Document (XML) ডেটা পাঠাতে সীমাবদ্ধ করেছে। আর না. নিম্নোক্ত প্রকারের যেকোনো একটি গ্রহণ করার জন্য একটি সংস্কার করা send() পদ্ধতি ওভাররাইড করা হয়েছে: DOMString , Document , FormData , Blob , File , ArrayBuffer । এই বিভাগের বাকি উদাহরণগুলি প্রতিটি প্রকার ব্যবহার করে ডেটা পাঠানোর প্রদর্শন করে।

স্ট্রিং ডেটা পাঠানো হচ্ছে: xhr.send(DOMSstring)

function sendText(txt) {
  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/server', true);
  xhr.onload = function(e) {
    if (this.status == 200) {
      console.log(this.responseText);
    }
  };

  xhr.send(txt);
}

sendText('test string');
function sendTextNew(txt) {
  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/server', true);
  xhr.responseType = 'text';
  xhr.onload = function(e) {
    if (this.status == 200) {
      console.log(this.response);
    }
  };
  xhr.send(txt);
}

sendTextNew('test string');

এখানে নতুন কিছু নেই, যদিও ডান স্নিপেটটি একটু ভিন্ন। এটি তুলনার জন্য responseType='text' সেট করে। আবার, সেই লাইনটি বাদ দিলে একই ফলাফল পাওয়া যায়।

ফর্ম জমা দেওয়া: xhr.send(FormData)

অনেক লোক সম্ভবত AJAX ফর্ম জমাগুলি পরিচালনা করতে jQuery প্লাগইন বা অন্যান্য লাইব্রেরি ব্যবহার করতে অভ্যস্ত। পরিবর্তে, আমরা FormData ব্যবহার করতে পারি, XHR2-এর জন্য অন্য একটি নতুন ডেটা টাইপ। জাভাস্ক্রিপ্টে একটি HTML <form> অন-দ্য-ফ্লাই তৈরি করার জন্য FormData সুবিধাজনক। সেই ফর্মটি তখন AJAX ব্যবহার করে জমা দেওয়া যেতে পারে:

function sendForm() {
  var formData = new FormData();
  formData.append('username', 'johndoe');
  formData.append('id', 123456);

  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/server', true);
  xhr.onload = function(e) { ... };

  xhr.send(formData);
}

মূলত, আমরা গতিশীলভাবে একটি <form> তৈরি করছি এবং এটিতে <input> মানগুলি ট্যাক করছি append পদ্ধতিতে কল করে।

অবশ্যই, আপনাকে স্ক্র্যাচ থেকে একটি <form> তৈরি করতে হবে না। FormData অবজেক্টগুলি পৃষ্ঠায় বিদ্যমান HTMLFormElement থেকে শুরু করা যেতে পারে। উদাহরণ স্বরূপ:

<form id="myform" name="myform" action="/server">
  <input type="text" name="username" value="johndoe">
  <input type="number" name="id" value="123456">
  <input type="submit" onclick="return sendForm(this.form);">
</form>
function sendForm(form) {
  var formData = new FormData(form);

  formData.append('secret_token', '1234567890'); // Append extra data before send.

  var xhr = new XMLHttpRequest();
  xhr.open('POST', form.action, true);
  xhr.onload = function(e) { ... };

  xhr.send(formData);

  return false; // Prevent page from submitting.
}

একটি HTML ফর্ম ফাইল আপলোড অন্তর্ভুক্ত করতে পারে (যেমন <input type="file"> ) এবং FormData এটিও পরিচালনা করতে পারে। শুধু ফাইল(গুলি) যুক্ত করুন এবং send() বলা হলে ব্রাউজার একটি multipart/form-data অনুরোধ তৈরি করবে:

function uploadFiles(url, files) {
  var formData = new FormData();

  for (var i = 0, file; file = files[i]; ++i) {
    formData.append(file.name, file);
  }

  var xhr = new XMLHttpRequest();
  xhr.open('POST', url, true);
  xhr.onload = function(e) { ... };

  xhr.send(formData);  // multipart/form-data
}

document.querySelector('input[type="file"]').addEventListener('change', function(e) {
  uploadFiles('/server', this.files);
}, false);

একটি ফাইল বা ব্লব আপলোড করা হচ্ছে: xhr.send(ব্লব)

আমরা XHR ব্যবহার করে File বা Blob ডেটাও পাঠাতে পারি। মনে রাখবেন সব File Blob , তাই হয় এখানে কাজ করে।

এই উদাহরণটি Blob() কনস্ট্রাক্টর ব্যবহার করে স্ক্র্যাচ থেকে একটি নতুন টেক্সট ফাইল তৈরি করে এবং সেই Blob সার্ভারে আপলোড করে। আপলোডের অগ্রগতি ব্যবহারকারীকে জানাতে কোডটি একটি হ্যান্ডলারও সেট আপ করে:

<progress min="0" max="100" value="0">0% complete</progress>
function upload(blobOrFile) {
  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/server', true);
  xhr.onload = function(e) { ... };

  // Listen to the upload progress.
  var progressBar = document.querySelector('progress');
  xhr.upload.onprogress = function(e) {
    if (e.lengthComputable) {
      progressBar.value = (e.loaded / e.total) * 100;
      progressBar.textContent = progressBar.value; // Fallback for unsupported browsers.
    }
  };

  xhr.send(blobOrFile);
}

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

বাইটের একটি অংশ আপলোড করা হচ্ছে: xhr.send(ArrayBuffer)

শেষ কিন্তু অন্তত নয়, আমরা ArrayBuffer s পাঠাতে পারি XHR এর পেলোড হিসাবে।

function sendArrayBuffer() {
  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/server', true);
  xhr.onload = function(e) { ... };

  var uInt8Array = new Uint8Array([1, 2, 3]);

  xhr.send(uInt8Array.buffer);
}

ক্রস অরিজিন রিসোর্স শেয়ারিং (CORS)

CORS একটি ডোমেনে থাকা ওয়েব অ্যাপ্লিকেশনগুলিকে অন্য ডোমেনে ক্রস ডোমেন AJAX অনুরোধ করার অনুমতি দেয়৷ এটি সক্ষম করা সহজ, শুধুমাত্র সার্ভার দ্বারা একটি একক প্রতিক্রিয়া শিরোনাম পাঠানোর প্রয়োজন৷

CORS অনুরোধ সক্রিয় করা হচ্ছে

ধরা যাক আপনার অ্যাপ্লিকেশনটি example.com এ থাকে এবং আপনি www.example2.com থেকে ডেটা তুলতে চান। সাধারণত আপনি যদি এই ধরনের AJAX কল করার চেষ্টা করেন, তাহলে অনুরোধটি ব্যর্থ হবে এবং ব্রাউজারটি একটি অরিজিন অমিল ত্রুটি নিক্ষেপ করবে। CORS এর সাথে, www.example2.com শুধুমাত্র একটি শিরোনাম যোগ করে example.com থেকে অনুরোধের অনুমতি দিতে পারে:

Access-Control-Allow-Origin: http://example.com

Access-Control-Allow-Origin একটি সাইটের অধীনে বা সমগ্র ডোমেন জুড়ে একটি একক সংস্থানে যোগ করা যেতে পারে। যেকোনো ডোমেনকে আপনাকে অনুরোধ করার অনুমতি দিতে, সেট করুন:

Access-Control-Allow-Origin: *

প্রকৃতপক্ষে, এই সাইটটি (html5rocks.com) তার সমস্ত পৃষ্ঠাগুলিতে CORS সক্ষম করেছে৷ বিকাশকারী সরঞ্জামগুলিকে ফায়ার করুন এবং আপনি আমাদের প্রতিক্রিয়াতে Access-Control-Allow-Origin দেখতে পাবেন:

html5rocks.com-এ অ্যাক্সেস-কন্ট্রোল-অ্যালো-অরিজিন হেডার
html5rocks.com-এ `অ্যাক্সেস-কন্ট্রোল-অ্যালো-অরিজিন` হেডার

ক্রস-অরিজিন অনুরোধগুলি সক্ষম করা সহজ, তাই দয়া করে, দয়া করে, দয়া করে CORS সক্ষম করুন যদি আপনার ডেটা সর্বজনীন হয়!

একটি ক্রস-ডোমেন অনুরোধ করা

যদি সার্ভার এন্ডপয়েন্ট CORS সক্ষম করে থাকে, তাহলে ক্রস-অরিজিন অনুরোধ করা সাধারণ XMLHttpRequest অনুরোধের চেয়ে আলাদা নয়। উদাহরণস্বরূপ, এখানে একটি অনুরোধ রয়েছে example.com এখন www.example2.com এ করতে পারে:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.example2.com/hello.json');
xhr.onload = function(e) {
  var data = JSON.parse(this.response);
  ...
}
xhr.send();

ব্যবহারিক উদাহরণ

ডাউনলোড করুন + HTML5 ফাইল সিস্টেমে ফাইল সংরক্ষণ করুন

ধরা যাক আপনার একটি ইমেজ গ্যালারি আছে এবং আপনি একগুচ্ছ ছবি আনতে চান তারপর HTML5 ফাইল সিস্টেম ব্যবহার করে স্থানীয়ভাবে সেভ করুন। এটি সম্পন্ন করার একটি উপায় হ'ল Blob হিসাবে চিত্রগুলিকে অনুরোধ করা এবং FileWriter ব্যবহার করে সেগুলি লিখুন:

window.requestFileSystem  = window.requestFileSystem || window.webkitRequestFileSystem;

function onError(e) {
  console.log('Error', e);
}

var xhr = new XMLHttpRequest();
xhr.open('GET', '/path/to/image.png', true);
xhr.responseType = 'blob';

xhr.onload = function(e) {

  window.requestFileSystem(TEMPORARY, 1024 * 1024, function(fs) {
    fs.root.getFile('image.png', {create: true}, function(fileEntry) {
      fileEntry.createWriter(function(writer) {

        writer.onwrite = function(e) { ... };
        writer.onerror = function(e) { ... };

        var blob = new Blob([xhr.response], {type: 'image/png'});

        writer.write(blob);

      }, onError);
    }, onError);
  }, onError);
};

xhr.send();

একটি ফাইল স্লাইস করা এবং প্রতিটি অংশ আপলোড করা

ফাইল এপিআই ব্যবহার করে, আমরা একটি বড় ফাইল আপলোড করার জন্য কাজটি ছোট করতে পারি। কৌশলটি হল আপলোডটিকে একাধিক খণ্ডে ভাগ করা, প্রতিটি অংশের জন্য একটি XHR তৈরি করা এবং ফাইলটিকে সার্ভারে একসাথে রাখা। GMail কিভাবে এত দ্রুত বড় সংযুক্তি আপলোড করে তার অনুরূপ। Google অ্যাপ ইঞ্জিনের 32MB HTTP অনুরোধের সীমার কাছাকাছি পেতেও এই ধরনের একটি কৌশল ব্যবহার করা যেতে পারে।

function upload(blobOrFile) {
  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/server', true);
  xhr.onload = function(e) { ... };
  xhr.send(blobOrFile);
}

document.querySelector('input[type="file"]').addEventListener('change', function(e) {
  var blob = this.files[0];

  const BYTES_PER_CHUNK = 1024 * 1024; // 1MB chunk sizes.
  const SIZE = blob.size;

  var start = 0;
  var end = BYTES_PER_CHUNK;

  while(start < SIZE) {
    upload(blob.slice(start, end));

    start = end;
    end = start + BYTES_PER_CHUNK;
  }
}, false);

})();

এখানে যা দেখানো হয়নি তা হল সার্ভারে ফাইলটি পুনর্গঠনের কোড।

তথ্যসূত্র