Einer der unbesungenen Helden im HTML5-Universum ist XMLHttpRequest
. Genau genommen ist XHR2 kein HTML5. Dies ist jedoch Teil der schrittweisen Verbesserungen, die Browser-Anbieter an der Kernplattform vornehmen. Ich nehme XHR2 in unsere neue Tüte mit Extras auf, weil es in den heutigen komplexen Web-Apps eine so wichtige Rolle spielt.
Es stellte sich heraus, dass unser alter Freund eine große Überarbeitung erfahren hat, aber viele Leute sind sich seiner neuen Funktionen nicht bewusst. XMLHttpRequest Level 2 bietet eine Reihe neuer Funktionen, die verrückten Hacks in unseren Web-Apps ein Ende setzen. Dinge wie Cross-Origin-Anfragen, das Hochladen von Fortschrittsereignissen und die Unterstützung für das Hochladen / Herunterladen von Binärdaten. Diese ermöglichen es AJAX, mit vielen der neuesten HTML5-APIs wie Dateisystem-API , Web-Audio-API und WebGL zusammenzuarbeiten.
In diesem Tutorial werden einige der neuen Funktionen in vorgestellt XMLHttpRequest
, insbesondere diejenigen, die zum Arbeiten mit Dateien verwendet werden können .
Daten abrufen
Das Abrufen einer Datei als binärer Blob war bei XHR schmerzhaft. Technisch war es nicht einmal möglich. Ein Trick, der gut dokumentiert wurde, besteht darin, den MIME-Typ mit einem benutzerdefinierten Zeichensatz zu überschreiben (siehe unten).
Der alte Weg, ein Bild zu holen:
var xhr = new XMLHttpRequest (); xhr . open ( 'GET' , '/path/to/image.png' , true ); // Hack, um Bytes unverarbeitet durchzuleiten. xhr . overrideMimeType ( 'text / plain; Zeichensatz = x-benutzerdefiniert' ); xhr . onreadystatechange = Funktion ( e ) { if ( this . readyState == 4 && this . status == 200 ) { var binStr = this . responseText ; für ( var i = 0 , len = binStr . Länge ; i < len ; ++ i ) { var c = binStr . charCodeAt ( i ); //String.fromCharCode(c & 0xff); var byte = c & 0xff ; // Byte am Offset i } } }; xhr . send ();
Während dies funktioniert, ist das, was Sie tatsächlich in den zurückbekommen, responseText
kein binärer Blob. Es ist eine binäre Zeichenfolge, die die Bilddatei darstellt. Wir bringen den Server dazu, die Daten unverarbeitet zurückzugeben. Obwohl dieses kleine Juwel funktioniert, werde ich es schwarze Magie nennen und davon abraten. Immer wenn Sie auf Zeichencode-Hacks und String-Manipulationen zurückgreifen, um Daten in ein gewünschtes Format zu zwingen, ist dies ein Problem.
Angeben eines Antwortformats
Im vorherigen Beispiel haben wir das Bild als binäre "Datei" heruntergeladen, indem wir den MIME-Typ des Servers überschrieben und den Antworttext als binäre Zeichenfolge verarbeitet haben. Nutzen wir stattdessen die XMLHttpRequest
neuen
Eigenschaften responseType
und response
Eigenschaften, um den Browser darüber zu informieren, in welchem Format die Daten zurückgegeben werden sollen.
- xhr. responseType
xhr.responseType
Stellen Sie vor dem Senden einer Anfrage je nach Ihren Datenanforderungen "Text", "Array-Puffer", "Blob" oder "Dokument" ein. Beachten Sie, dass beim Festlegenxhr.responseType = ''
(oder Weglassen) standardmäßig die Antwort auf "Text" erfolgt.- xhr. Antwort
- Nach einer erfolgreichen Anfrage, die Antwort Unterkunft xhr wird die angeforderten Daten als enthält
DOMString
,ArrayBuffer
,Blob
, oderDocument
(je nachdem , was für gesetzt wurderesponseType
.)
Mit dieser neuen Attraktivität können wir das vorherige Beispiel überarbeiten, aber dieses Mal rufen Sie das Bild als Blob
Zeichenfolge ab:
var xhr = new XMLHttpRequest (); xhr . open ( 'GET' , '/path/to/image.png' , true ); xhr . responseType = 'blob' ; xhr . onload = Funktion ( e ) { wenn ( dies . Status == 200 ) { // Hinweis: .response statt .responseText var blob = neue Blob ([ diese . Antwort ], { Typ : 'image / jpeg' }); ... } }; xhr . send ();
Viel schöner!
ArrayBuffer-Antworten
An ArrayBuffer
ist ein generischer Container mit fester Länge für Binärdaten. Sie sind sehr praktisch, wenn Sie einen allgemeinen Puffer mit Rohdaten benötigen. Die eigentliche Stärke dieser Daten besteht jedoch darin, dass Sie mithilfe von JavaScript-typisierten Arrays "Ansichten" der zugrunde liegenden Daten erstellen können . Tatsächlich können mehrere Ansichten aus einer einzigen ArrayBuffer
Quelle erstellt werden. Beispielsweise könnten Sie ArrayBuffer
aus denselben Daten ein 8-Bit-Integer-Array erstellen, das dasselbe wie ein vorhandenes 32-Bit-Integer-Array verwendet. Die zugrunde liegenden Daten bleiben gleich, wir erstellen lediglich unterschiedliche Darstellungen davon.
Im Folgenden wird beispielsweise dasselbe Bild wie ein abgerufen ArrayBuffer
, diesmal wird jedoch aus diesem Datenpuffer ein vorzeichenloses 8-Bit-Integer-Array erstellt:
var xhr = new XMLHttpRequest (); xhr . open ( 'GET' , '/path/to/image.png' , true ); xhr . responseType = 'arraybuffer' ; xhr . onload = Funktion ( e ) { var uInt8Array = neuer Uint8Array ( dies . Reaktion ); // this.response == uInt8Array.buffer // var byte3 = uInt8Array [4]; // Byte bei Offset 4 ... }; xhr . send ();
Blob-Antworten
Wenn Sie direkt mit a arbeiten möchten Blob
und / oder keines der Bytes der Datei bearbeiten müssen, verwenden Sie xhr.responseType='blob'
:
Fenster . URL = Fenster . URL || Fenster . webkitURL ; // Achten Sie auf Herstellerpräfixe. var xhr = new XMLHttpRequest (); xhr . open ( 'GET' , '/path/to/image.png' , true ); xhr . responseType = 'blob' ; xhr . onload = Funktion ( e ) { if ( this . status == 200 ) { var blob = this . Antwort ; var img = Dokument . createElement ( 'img' ); img . onload = Funktion ( e ) { Fenster . URL . revokeObjectURL ( img . src ); // Aufräumen nach dir. }; img . src = Fenster . URL . createObjectURL ( Blob ); Dokument . Körper . appendChild ( img ); ... } }; xhr . send ();
A Blob
kann an verschiedenen Stellen verwendet werden, z. B. zum Speichern in indexedDB , zum Schreiben in das HTML5- Dateisystem oder zum Erstellen einer Blob-URL (siehe Beispiel).
Daten senden
Es ist großartig, Daten in verschiedenen Formaten herunterladen zu können , aber es bringt uns nicht weiter, wenn wir diese umfangreichen Formate nicht an die Heimatbasis (den Server) zurücksenden können.
XMLHttpRequest
hat uns seit einiger Zeit auf das Senden DOMString
oder Document
(XML) von Daten beschränkt. Nicht länger. Ein erneuertes send()
Verfahren wurde mit einen der folgenden Typen zu akzeptieren , außer Kraft gesetzt:
DOMString
, Document
, FormData
, Blob
,
File
, ArrayBuffer
. Die Beispiele im Rest dieses Abschnitts zeigen das Senden von Daten mit jedem Typ.
Senden von Zeichenfolgendaten: xhr.send (DOMString)
Funktion sendText ( txt ) { var xhr = new XMLHttpRequest (); xhr . open ( 'POST' , '/ server' , true ); xhr . onload = Funktion ( e ) { if ( this . status == 200 ) { Konsole . log ( this . responseText ); } }; xhr . send ( txt ); }} sendText ( ' Teststring ' ); Funktion sendTextNew ( txt ) { var xhr = new XMLHttpRequest (); xhr . open ( 'POST' , '/ server' , true ); xhr . responseType = 'text' ; xhr . onload = Funktion ( e ) { if ( this . status == 200 ) { Konsole . log ( dies . Antwort ); } }; xhr . send ( txt ); }} sendTextNew ( ' Teststring ' );
Hier gibt es nichts Neues, obwohl das richtige Snippet etwas anders ist. Es setzt responseType='text'
zum Vergleich. Das Weglassen dieser Zeile führt wiederum zu denselben Ergebnissen.
Formulare senden: xhr.send (FormData)
Viele Leute sind wahrscheinlich daran gewöhnt, jQuery-Plugins
oder andere Bibliotheken zu verwenden, um AJAX-Formularübermittlungen zu verarbeiten. Stattdessen können wir einen FormData
anderen neuen Datentyp verwenden, der für XHR2 konzipiert wurde. FormData
ist praktisch, um HTML <form>
im laufenden Betrieb in JavaScript zu erstellen . Dieses Formular kann dann mit AJAX eingereicht werden:
Funktion sendForm () { var formData = new FormData (); formData . anhängen ( 'Benutzername' , 'Johndoe' ); formData . anhängen ( 'id' , 123456 ); var xhr = new XMLHttpRequest (); xhr . open ( 'POST' , '/ server' , true ); xhr . onload = Funktion ( e ) { ... }; xhr . send ( formData ); }}
Im Wesentlichen erstellen wir nur dynamisch a <form>
und greifen
<input>
Werte an, indem wir die Append-Methode aufrufen.
Natürlich müssen Sie keine <form>
von Grund auf neu erstellen .
FormData
Objekte können von HTMLFormElement
der Seite aus initialisiert werden und auf dieser vorhanden sein
. Beispielsweise:
Form ); " > Funktion sendForm ( Formular ) { var formData = new FormData ( Formular ); formData . append ( 'secret_token' , '1234567890' ); // Zusätzliche Daten vor dem Senden anhängen. var xhr = new XMLHttpRequest (); xhr . open ( 'POST' , form . action , true ); xhr . onload = Funktion ( e ) { ... }; xhr . send ( formData ); return false ; // Verhindert, dass eine Seite gesendet wird. }}
Ein HTML-Formular kann Datei-Uploads enthalten (z. B. <input type="file">
) und FormData
auch damit umgehen. Hängen Sie einfach die Datei (en) an und der Browser erstellt eine multipart/form-data
Anfrage, wenn er send()
aufgerufen wird:
Funktion uploadFiles ( URL , Dateien ) { var formData = new FormData (); für ( var i = 0 , Datei ; Datei = Dateien [ i ]; ++ i ) { formData . append ( Datei . Name , Datei ); }} var xhr = new XMLHttpRequest (); xhr . open ( 'POST' , url , true ); xhr . onload = Funktion ( e ) { ... }; xhr . send ( formData ); // mehrteilig / Formulardaten } Dokument . querySelector ( 'input [type = "file"]' ). addEventListener ( 'change' , Funktion ( e ) { uploadFiles ( '/ server' , this . files ); }, false );
Hochladen einer Datei oder eines Blobs: xhr.send (Blob)
Wir können auch Daten mit XHR senden File
oder senden Blob
. Denken Sie daran, dass alle File
s s sind Blob
, also funktioniert beides hier.
In diesem Beispiel wird mithilfe des Blob()
Konstruktors eine neue Textdatei von Grund auf neu erstellt und Blob
auf den Server hochgeladen. Der Code richtet auch einen Handler ein, der den Benutzer über den Fortschritt des Uploads informiert:
Hochladen eines Bytes: xhr.send (ArrayBuffer)
Last but not least können wir ArrayBuffer
s als Nutzlast des XHR senden .
Funktion sendArrayBuffer () { var xhr = new XMLHttpRequest (); xhr . open ( 'POST' , '/ server' , true ); xhr . onload = Funktion ( e ) { ... }; var uInt8Array = neues Uint8Array ([ 1 , 2 , 3 ]); xhr . send ( uInt8Array . buffer ); }}
Cross Origin Resource Sharing (CORS)
Mit CORS können Webanwendungen in einer Domäne domänenübergreifende AJAX-Anforderungen an eine andere Domäne senden. Es ist kinderleicht zu aktivieren, da nur ein einziger Antwortheader vom Server gesendet werden muss.
Aktivieren von CORS-Anforderungen
Angenommen, Ihre Anwendung lebt weiter
example.com
und Sie möchten Daten abrufen
www.example2.com
.
Wenn Sie versuchen, diese Art von AJAX-Aufruf durchzuführen, schlägt die Anforderung normalerweise fehl
und der Browser gibt einen Fehler bei der Nichtübereinstimmung des Ursprungs aus. Mit CORS
www.example2.com
können Sie festlegen, dass Anforderungen example.com
durch einfaches Hinzufügen eines Headers
zugelassen werden sollen:
Zugriff - Kontrolle - Zulassen - Ursprung : http : //example.com Zugriff - Kontrolle - Zulassen - Herkunft : *
Access-Control-Allow-Originkann einer einzelnen Ressource unter einer Site oder in der gesamten Domäne hinzugefügt werden. Damit jede Domain eine Anfrage an Sie machen, setzen:
Zugriff - Kontrolle - Zulassen - Herkunft : *
Tatsächlich hat diese Site (html5rocks.com)
CORS auf allen Seiten aktiviert. Starten Sie die Entwicklertools und Sie werden Folgendes
Access-Control-Allow-Origin
in unserer Antwort sehen:
Das Aktivieren von Cross-Origin-Anfragen ist einfach. Bitte, bitte, bitte aktivieren Sie CORS, wenn Ihre Daten öffentlich sind!
Eine domänenübergreifende Anfrage stellen
Wenn der Serverendpunkt CORS aktiviert hat, unterscheidet sich das Herstellen der Cross-Origin-Anforderung nicht von einer normalen XMLHttpRequest
Anforderung. Hier ist zum Beispiel eine Anfrage, example.com
die jetzt gestellt werden kann an
www.example2.com
:
var xhr = new XMLHttpRequest (); xhr . open ( 'GET' , 'http://www.example2.com/hello.json' ); xhr . onload = Funktion ( e ) { var data = JSON . Parsen ( diese . Reaktion ); ... } xhr . send ();
Praktische Beispiele
Laden Sie Dateien in das HTML5-Dateisystem herunter und speichern Sie sie
Angenommen, Sie haben eine Bildergalerie und möchten eine Reihe von Bildern abrufen und diese dann lokal mit dem HTML5-Dateisystem speichern . Eine Möglichkeit, dies zu erreichen, besteht darin, Bilder als Blob
s anzufordern und sie mit FileWriter
folgenden Worten zu schreiben :
Fenster . requestFileSystem = Fenster . requestFileSystem || Fenster . webkitRequestFileSystem ; Funktion onError ( e ) { Konsole . log ( 'Fehler' , e ); }} var xhr = new XMLHttpRequest (); xhr . open ( 'GET' , '/path/to/image.png' , true ); xhr . responseType = 'blob' ; xhr . Onload = Funktion ( e ) { Fenster . requestFileSystem ( TEMPORARY , 1024 * 1024 , Funktion ( fs ) { fs . root . getFile ( 'image.png' , { create : true }, Funktion ( fileEntry ) { fileEntry . createWriter ( Funktion ( Writer ) { Schriftsteller . onwrite = Funktion ( e ) { ... }; Schriftsteller . onerror = function ( e ) { ... }; var blob = neuer Blob ([ xhr . Antwort ], { Typ : 'image / png' }); Schriftsteller . schreiben ( Blob ); }, onError ); }, onError ); }, onError ); }; xhr . send ();
Hinweis: Informationen zur Verwendung dieses Codes finden Sie im Tutorial " Erkunden der Dateisystem-APIs " unter Browserunterstützung und Speicherbeschränkungen .
Schneiden Sie eine Datei und laden Sie jeden Teil hoch
Mithilfe der Datei-APIs können wir den Aufwand für das Hochladen einer großen Datei minimieren. Die Technik besteht darin, den Upload in mehrere Blöcke zu unterteilen, eine XHR für jeden Teil zu erzeugen und die Datei auf dem Server zusammenzustellen. Dies ähnelt dem schnellen Hochladen großer Anhänge durch GMail. Eine solche Technik könnte auch verwendet werden, um das 32-MB-http-Anforderungslimit von Google App Engine zu umgehen.
Funktion upload ( blobOrFile ) { var xhr = new XMLHttpRequest (); xhr . open ( 'POST' , '/ server' , true ); xhr . onload = Funktion ( e ) { ... }; xhr . send ( blobOrFile ); }} Dokument . querySelector ( 'input [type = "file"]' ). addEventListener ( 'change' , Funktion ( e ) { var blob = this . files [ 0 ]; const BYTES_PER_CHUNK = 1024 * 1024 ; // 1 MB Blockgröße. const SIZE = Blob . Größe ; var start = 0 ; var end = BYTES_PER_CHUNK ; while ( Start < GRÖSSE ) { Upload ( Blob . Slice ( Start , Ende )); Start = Ende ; end = start + BYTES_PER_CHUNK ; } }, false ); }) ();
Was hier nicht angezeigt wird, ist der Code zum Rekonstruieren der Datei auf dem Server.
Versuch es!