📜 ⬆️ ⬇️

New XMLHttpRequest2 Features

One of the unsung heroes of the HTML5 universe is XMLHttpRequest 2. Strictly speaking, XHR2 is not part of HTML5 and is not an independent object. XHR2 is the same XMLHttpRequest, but with some changes. XHR2 is an integral part of complex web applications, so it should be given more attention.

Our old friend XMLHttpRequest has changed a lot, but not many people know about its changes. XMLHttpRequest Level 2 includes new features that will put an end to our crazy hacks and tambourine dances around XMLHttpRequest: cross-domain queries, the process of downloading files, downloading and sending binary data. These features enable AJAX to work confidently without any hacks with the latest HTML5 technologies: File System API , Web Audio API , and WebGL.

This article will highlight the new features of XMLHttpRequest, especially those that can be used when working with files .

Data retrieval


Extracting binary data from a file to XHR is very painful. Technically, it is even impossible. But there is one well documented trick that allows you to rewrite the mime type with a custom encoding.
')
So before you could get the contents of the image:
var xhr = new XMLHttpRequest(); xhr.open('GET', '/path/to/image.png', true); //   ,      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; } } }; xhr.send(); 

Although this works, you do not receive a binary blob in responseText, but a binary string that represents a binary image file. We deceive XMLHttpRequest and force it to pass the raw data. Although this is a small hack, but I want to call it black magic.

Specify response format


In the previous example, we loaded the image as a “binary file”, rewriting the server mime type and processing it as a binary string. Instead of this magic, let's take advantage of the new XMLHttpRequest feature — the responseType and response properties, which will show the browser in which format we want to receive data.

xhr.responseType
Before sending a request, you can change the xhr.responseType property and specify the output format: “text”, “arraybuffer”, “blob” or “document” (default is “text”).

xhr.response
After a successful request is completed, the response property will contain the requested data in the format DOMString, ArrayBuffer, Blob, or Document in accordance with responseType.

With this new great feature we can remake the previous example. This time we request a picture as ArrayBuffer instead of a string. We upload the downloaded file into Blob format using BlobBuilder API:
 BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder; var xhr = new XMLHttpRequest(); xhr.open('GET', '/path/to/image.png', true); xhr.responseType = 'arraybuffer'; xhr.onload = function(e) { if (this.status == 200) { var bb = new BlobBuilder(); bb.append(this.response); // :  xhr.responseText var blob = bb.getBlob('image/png'); /*...*/ } }; xhr.send(); 

That's so much better!

ArrayBuffer format answers

ArrayBuffer is a generic fixed-length container for binary data. This is very convenient if you need a generic raw binary data buffer, but the real strength of ArrayBuffer is that you can make a typed JavaScript array out of it . In fact, you can create arrays of different lengths using a single ArrayBuffer. For example, you can create an 8-bit integer array that uses the same ArrayBuffer as the 32-bit array obtained from the same data.

As an example, let's write the code that receives our image as an ArrayBuffer and creates an 8-bit integer array from its data:
 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]; // 4-  /*...*/ }; xhr.send(); 

Blob Answers

If you want to work directly with Blob and / or you do not need to manipulate the file bytes, use xhr.responseType='blob' (Now available only in Chrome crbug.com/52486 ):
 window.URL = window.URL || window.webkitURL; 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 can be used in several places: saving data to indexedDB , writing to HTML5 File System , creating Blob URL ( MDC ) as in the example above.

Sending data


The ability to receive data in various formats is great, but it does not suit us if we cannot send this data back (to the server). XMLHttpRequest limited us to sending a DOMString or Document (XML). Now it is in the past. The updated send () method allows you to send data of the following types: DOMString, Document, FormData, Blob, File, ArrayBuffer. In this part of the article we will look at how to send data in these formats.

Sending string data: xhr.send (DOMString)

Before XMLHttpRequest 2:
 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'); 

After XMLHttpRequest 2:
 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); } sendText2('test string'); 

Nothing new. The “After” example is slightly different. It explicitly defines responseType, but you can omit responseType and get the same result (the default is always text).

Submitting Form Data: xhr.send (FormData)

I think many of you used jQuery or other libraries to send form data over AJAX. Instead, we can use FormData, another data type that XHR2 understands. FormData is useful for creating HTML forms on the fly in JavaScript. These forms can be sent using 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); // <<< 

In essence, we dynamically create a form and add input fields to it by calling the append method.
And you do not need to create a real form from scratch. FormData objects can be initialized from existing HTMLFormElement elements on the page. For example:
 <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  HTMLFormElement formData.append('secret_token', '1234567890'); //      var xhr = new XMLHttpRequest(); xhr.open('POST', form.action, true); xhr.onload = function(e) { /*...*/ }; xhr.send(formData); return false; //   } 

HTML form can contain files ( <input type="file"> ) - FormData can work with them. Just add the file (s) and the browser will execute a multipart/form-data request when the send() method is called. It is very convenient!
 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); 

Sending a file or blob: xhr.send (Blob)

Using XHR2 we can also send File or Blob. Keep in mind that the files are Blob.
In this example, we will create a new text field from scratch using the BlobBuilder API and upload this Blob to the server. This code also creates a handler that shows us the file upload process (An incredibly useful HTML5 feature):
 <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) { /*...*/ }; //     var progressBar = document.querySelector('progress'); xhr.upload.onprogress = function(e) { // <<< if (e.lengthComputable) { progressBar.value = (e.loaded / e.total) * 100; progressBar.textContent = progressBar.value; //      progress } }; xhr.send(blobOrFile); // <<< } var BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder; var bb = new BlobBuilder(); bb.append('hello world'); // <<< upload(bb.getBlob('text/plain')); // <<< 

Sending an arbitrary set of bytes: xhr.send (ArrayBuffer)

We can send ArrayBuffers
 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); // <<<< } 

Cross Origin Resource Sharing (CORS)


CORS allows applications on the same domain to perform cross-domain AJAX requests to another domain. We don’t even need to change anything on the client - everything is extremely simple! The browser itself will send the necessary title for us.

Enable CORS Requests

Suppose our application is on example.com and we need to get data from www.example2.com www.example2.com . Usually, if you try to make such an AJAX request, the request will not be executed and the browser will throw the exception “origin mismatch”. With CORS www.example2.com www.example2.com may decide to allow our application to execute the request from example.com or not by adding just one header:
 Access-Control-Allow-Origin: http://example.com 

The Access-Control-Allow-Origin header can be issued to one site or to any site from any domain:
 Access-Control-Allow-Origin: * 

CORS is enabled on any page of the html5rocks.com site. If you enable the debugger, then you can see this Access-Control-Allow-Origin header:
image
Enable cross-domain queries is very simple. If your data is accessible to everyone, then please enable CORS !

Creating a cross-domain query

If the server resource permits CORS, then creating a cross-domain request is no different from the usual XMLHttpRequest. For example, this is how we can execute a request from an application on the example.com server to the server www.example2.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(); 

Everything is extremely transparent and there are no dances with a tambourine around postMessage, window.name, document.domain, server proxies and other perversions of the methods .

Examples


Upload and save file in HTML5 File System

Suppose we have a gallery of images and we want to save some pictures to ourselves using the HTML5 File System .
 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 = 'arraybuffer'; // <<< //     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) { /*...*/ }; //  Blob    var bb = new BlobBuilder(); // <<< bb.append(this.response); // <<< //    writer.write(bb.getBlob('image/png')); // <<< }, onError); }, onError); }, onError); }; xhr.send(); 

Attention: see which browsers support FileSystem API

Sending a file in parts

Using the File API we can simplify the process of sending a large file. We split a large file into several small files, then each one is sent using XHR. On the server we collect the file in one big. This is similar to GMail sending large attachments. This technique can be used to bypass the limitations of Google App Engine - 32MB per http request.
 window.BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder || window.BlobBuilder; 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 const SIZE = blob.size; var start = 0; var end = BYTES_PER_CHUNK; while(start < SIZE) { // : blob.slice  .  http://goo.gl/U9mE5 if ('mozSlice' in blob) { var chunk = blob.mozSlice(start, end); } else { var chunk = blob.webkitSlice(start, end); } upload(chunk); start = end; end = start + BYTES_PER_CHUNK; } }, false); })(); 

I don’t attach the file assembly script on the server - everything is obvious there.

Links


1. XMLHttpRequest Level 2 Specification
2. Cross Origin Resource Sharing Specification (CORS)
3. File API specification
4. FileSystem API specification

Source: https://habr.com/ru/post/120917/


All Articles