it-swarm.com.de

Kann ich einen asynchronen, domänenübergreifenden Datei-Upload durchführen?

Es ist möglich! Lesen Sie unten.


Zunächst möchte ich anhand dieses Diagramms erläutern, wie asynchrone Dateiuploads erreicht werden können:


Sorry. Ich habe eine meiner Domains heruntergefahren, und das Bild ist jetzt verschwunden. Es war jedoch ein wirklich schönes Bild. Bevor ich herausfand, dass Stack Overflow das Hochladen von Bildern über Imgur ermöglicht. 


Wie Sie sehen können, besteht der Trick darin, die HTTP-Antwort in ein verstecktes IFRAME-Element laden zu lassen, anstatt auf die Seite selbst. (Dies geschieht durch Festlegen der target-Eigenschaft des FORM-Elements, wenn Sie die FORM mit JavaScript absenden.)

Das funktioniert. Das Problem, vor dem ich stehe, ist jedoch, dass sich das serverseitige Skript in einer anderen Domäne befindet. Das FORM-Submit ist eine domänenübergreifende HTTP-Anforderung. Jetzt hat das serverseitige Skript CORS aktiviert. Dies gibt meiner Webseite die Rechte, die Antwortdaten von HTTP-Anforderungen, die von meiner Seite an dieses Skript gestellt werden, zu lesen. Dies funktioniert jedoch nur, wenn ich die HTTP-Antwort über Ajax erhalte. ergo, JavaScript.

In diesem Fall ist die Antwort jedoch auf das IFRAME-Element gerichtet. Sobald die XML-Antwort im IFRAME gelandet ist, wird die URL zum Entfernen des Skripts - z. http://remote-domain.com/script.pl.

Leider behandelt CORS diesen Fall nicht (zumindest denke ich) - Ich kann den Inhalt des IFRAME nicht lesen, da seine URL nicht mit der URL der Seite (andere Domäne) übereinstimmt. Ich erhalte diesen Fehler:

Unsicherer JavaScript-Versuch, auf den Frame mit der URL .__ zuzugreifen. hxxp: //remote-domain.com/script.pl vom Frame mit der URL hxxp: //mein-domain.de/outer.html. Domänen, Protokolle und Ports müssen Spiel.

Und da der Inhalt des IFRAME ein XML-Dokument ist, befindet sich im IFRAME kein JavaScript-Code, der postMessage oder etwas verwenden könnte.

Meine Frage ist also: Wie kann ich den XML-Inhalt vom IFRAME beziehen?

Wie bereits erwähnt, kann ich domänenübergreifende HTTP-Antworten direkt abrufen (CORS-fähig), aber es scheint, dass ich keine domänenübergreifenden HTTP-Antworten lesen kann, sobald sie in einen IFRAME geladen werden.

Und als ob diese Frage nicht unlösbar genug wäre, lassen Sie mich diese Lösungen ausschließen:

  1. easyXDM und ähnliche Techniken, die einen Endpunkt in der Remote-Domäne erfordern,

  2. Ändern der XML-Antwort (um ein SCRIPT-Element aufzunehmen), 

  3. serverseitiger Proxy - Ich verstehe, dass ich in meiner Domain ein serverseitiges Skript haben könnte, das als Proxy dienen könnte.

Kann man dies, abgesehen von diesen beiden Lösungen, auch tun?


Es kann gemacht werden !!

Es stellt sich heraus, dass es möglich ist, eine XHR-Anfrage (Ajax-Anfrage) zu fälschen, die ein multipart/form-data-FORM-Submit imitiert (das im obigen Bild zum Hochladen der Datei auf den Server verwendet wird).

Der Trick ist die Verwendung von FormData constructor - lesen Sie diesen Mozilla Hacks-Artikel , um weitere Informationen zu erhalten.

Das ist wie man es macht:

// STEP 1
// retrieve a reference to the file
// <input type="file"> elements have a "files" property
var file = input.files[0];

// STEP 2
// create a FormData instance, and append the file to it
var fd = new FormData();
fd.append('file', file);

// STEP 3 
// send the FormData instance with the XHR object
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://remote-domain.com/script.pl', true);
xhr.onreadystatechange = responseHandler;
xhr.send(fd);

Die obige Methode führt einen asynchronen Datei-Uplaod aus, der dem regulären Datei-Upload entspricht, der im Bild oben beschrieben ist und durch Senden dieses Formulars erreicht wird:

<form action="http://remote-domain.com/script.pl" 
        enctype="multipart/form-data" method="post">
    <input type="file" name="file">
</form>

Wie ein Chef :)

47
Šime Vidas

Senden Sie einfach eine domänenübergreifende XHR-Anfrage mit den Daten aus dem Formular, anstatt das Formular zu senden. CORS ist nur für die erstere.

Wenn Sie es anders machen müssen, verhandeln Sie mit postMessage mit dem Frame.

Und da der Inhalt des IFRAME ein XML-Dokument ist, befindet sich im IFRAME kein JavaScript-Code, der PostMessage oder etwas verwenden könnte.

Wie hält dich das auf? Fügen Sie ein Skriptelement unter dem HTML- oder SVG-Namespace (<script xmlns="http://www.w3.org/1999/xhtml" type="application/ecmascript" src="..."/>) an einer beliebigen Stelle im XML-Code ein.

9
Eli Grey

Ich denke, dass es nicht mit der Art und Weise, wie Sie sie beschreiben, zu erledigen ist. Wenn Sie domänenübergreifende Probleme haben, können Sie es normalerweise mit einem JSONp-Ansatz lösen. Dies funktioniert jedoch nur für GET-Anforderungen. Mit HTML5 könnten Sie möglicherweise binär mit der GET-Anfrage senden, aber das ist nur ein wenig komisch.

  • Eine Lösung wäre, den Remote-Webservice lokal bereitzustellen, indem er die Anforderung auf dem lokalen Webserver weiterleitet. Dies führt zu einer zusätzlichen Belastung Ihres lokalen Webservers, sodass ich mir vorstellen kann, dass dies unmöglich ist. Wenn die Dateien jedoch klein und selten sind, wird dies gut funktionieren.

  • Eine andere Lösung wäre, den Server nach dem Senden der Datei abzufragen. Sie können ein Token mitsenden und den Status des Servers mit regulärem JSONp abfragen. Auf diese Weise müssen Sie nicht aus dem iframe lesen.

  • Legen Sie die gesamte Seite in einen Iframe, der auf dem Remote-Server ausgeführt wird. Dies könnte das Problem nur verschieben, aber wenn die XML-Ausgabe der letzte Schritt in einem Prozess ist, ist das durchaus machbar.

Ich bin sicher, Sie haben gute Gründe dafür, dass sich der Verarbeitungsserver auf einer anderen Domäne befindet. Wenn dies nicht der Fall wäre, hätten Sie all diese Probleme nicht. Vielleicht lohnt es sich zu überdenken?

0
Halcyon