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 XMLHttpRequestneuen Eigenschaften responseTypeund responseEigenschaften, 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 Festlegen xhr.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, oder Document(je nachdem , was für gesetzt wurde responseType.)

Mit dieser neuen Attraktivität können wir das vorherige Beispiel überarbeiten, aber dieses Mal rufen Sie das Bild als BlobZeichenfolge 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 ArrayBufferQuelle 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 Blobund / 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 Blobkann 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. XMLHttpRequesthat 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 FormDataanderen 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 . FormDataObjekte 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 FormDataauch damit umgehen. Hängen Sie einfach die Datei (en) an und der Browser erstellt eine multipart/form-dataAnfrage, 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 Fileoder senden Blob. Denken Sie daran, dass alle Files s sind Blob, also funktioniert beides hier.

In diesem Beispiel wird mithilfe des Blob()Konstruktors eine neue Textdatei von Grund auf neu erstellt und Blobauf den Server hochgeladen. Der Code richtet auch einen Handler ein, der den Benutzer über den Fortschritt des Uploads informiert:

 0% abgeschlossen    
Funktion upload ( blobOrFile ) { var xhr = new XMLHttpRequest (); 
  xhr . open ( 'POST' , '/ server' , true ); 
  xhr . onload = Funktion ( e ) { ... }; 
          

  // Höre dir den Upload-Fortschritt an. var progressBar = Dokument . querySelector ( 'progress' ); xhr . hochladen . onprogress = Funktion ( e ) { if ( e . lengthComputable ) { 
      progressBar . Wert = ( e . geladen / e . gesamt ) * 100 ; 
      progressBar . textContent =
  
     
         progressBar . Wert ; // Fallback für nicht unterstützte Browser. } }; 
    
 
  xhr . send ( blobOrFile ); }}

Upload ( neuer Blob ([ 'Hallo Welt' ], { Typ : 'Text / Klartext' })    );
    

Hochladen eines Bytes: xhr.send (ArrayBuffer)

Last but not least können wir ArrayBuffers 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.comund 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:

Access-Control-Allow-Origin-Header auf html5rocks.com
Access-Control-Allow-Origin Header auf html5rocks.com

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 XMLHttpRequestAnforderung. Hier ist zum Beispiel eine Anfrage, example.comdie 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 Blobs anzufordern und sie mit FileWriterfolgenden 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!

# Bytes / Chunk: