Est-il possible d'effectuer un téléchargement de fichier multi-domaine asynchrone?

C'est possible! Lire ci-dessous.


Tout d'abord, permettez-moi d'utiliser ce diagramme pour expliquer comment les téléchargements de fichiers asynchrones peuvent être obtenus:


Pardon. J'ai fermé l'un de mes domaines et l'image disparue maintenant. C'était vraiment une belle image. Ce fut avant que je découvre que Stack Overflow permet de télécharger des images via Imgur.


Comme vous pouvez le voir, l'astuce consiste à laisser la charge de réponse HTTP dans un élément IFRAME caché au lieu de la page elle-même. (Cela se fait en définissant la propriété target de l'élément FORM lors de la soumission du formulaire avec JavaScript.)

Cela marche. Cependant, le problème auquel je suis confronté est que le script côté serveur se trouve sur un domaine différent . Le FORM-submit est une requête HTTP multi-domaine. Maintenant, le script côté serveur a activé CORS qui donne à ma page Web les droits de lire les données de réponse des requêtes HTTP faites de ma page à ce script, mais cela fonctionne uniquement si je reçois la réponse HTTP via Ajax, Ergo, JavaScript.

Cependant, dans ce cas, la réponse est dirigée vers l'élément IFRAME. Et une fois que la réponse XML atterrit dans l'IFRAME, son URL sera le script de suppression – par exemple http://remote-domain.com/script.pl .

Malheureusement, CORS ne couvre pas ce cas (au moins je pense) – Je ne peux pas lire le contenu de l'IFRAME car son URL ne correspond pas à l'URL de la page (domaine différent). Je reçois cette erreur:

La tentative de JavaScript dangereuse d'accéder au cadre avec l'URL hxxp: //remote-domain.com/script.pl à partir du cadre avec l'URL hxxp: //my-domain.com/outer.html. Les domaines, les protocoles et les ports doivent correspondre.

Et puisque le contenu de l'IFRAME est un document XML, il n'y a pas de code JavaScript dans l'IFRAME qui pourrait utiliser postMessage ou quelque chose.

Donc, ma question est la suivante: Comment puis-je obtenir le contenu XML de l'IFRAME?

Comme je l'ai dit plus haut, je peux récupérer directement les réponses HTTP entre domaines (CORS activé), mais il me semble que je ne peux pas lire les réponses HTTP multi-domaines une fois qu'elles sont chargées dans un IFRAME.

Et comme si cette question n'était pas insoluble, permettez-moi d' exclure ces solutions :

  1. EasyXDM et des techniques similaires nécessitant un point final sur le domaine distant,

  2. Modification de la réponse XML (pour inclure un élément SCRIPT),

  3. Proxy côté serveur – Je comprends que je pourrais avoir un script côté serveur sur mon domaine qui pourrait servir de proxy.

Donc, en dehors de ces deux solutions, cela peut-il être fait?


Ça peut être fait!!

Il s'avère qu'il est possible de forger une requête XHR (requête Ajax) qui imite une soumission de multipart/form-data (qui est utilisée dans l'image ci-dessus pour télécharger le fichier sur le serveur).

L'astuce consiste à utiliser le constructeur FormData : lisez cet article Mozilla Hacks pour plus d'informations.

Voici comment vous le faites:

 // 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); 

La méthode ci-dessus exécute un fichier asynchrone-uplaod, ce qui équivaut au chargement régulier de fichier décrit dans l'image ci-dessus et obtenu en soumettant ce formulaire:

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

Comme un pro 🙂

Il suffit d'envoyer une requête XHR entre domaines avec les données du formulaire au lieu de soumettre le formulaire. CORS est seulement pour le premier.

Si vous devez le faire dans l'autre sens, négocier avec le cadre en utilisant PostMessage.

Et puisque le contenu de l'IFRAME est un document XML, il n'y a pas de code JavaScript dans IFRAME qui pourrait utiliser PostMessage ou quelque chose.

Comment cela vous arrête-t-il? Inclure un élément de script sous l'espace de noms HTML ou SVG ( <script xmlns="http://www.w3.org/1999/xhtml" type="application/ecmascript" src="..."/> ) n'importe où dans le XML.

Je pense que cela ne peut pas être fait avec la façon dont vous décrivez. Normalement, si vous avez des problèmes de domaine croisés, vous pouvez le résoudre par une approche JSONp, mais cela ne fonctionne que pour les demandes GET. Avec HTML5, vous pouvez potentiellement envoyer des binaires avec la requête GET, mais cela ne suffit pas.

  • Une solution serait de rendre localisé le service Web distant en proposant la demande sur le serveur Web local. Cela entraînera une charge supplémentaire pour votre serveur web local afin que je puisse imaginer qu'il soit impossible. Si les fichiers sont petits et peu fréquents, cela va bien.

  • Une autre solution serait de commencer à interroger le serveur après avoir envoyé le fichier. Vous pouvez envoyer un jeton et rechercher le statut du serveur en utilisant JSONp régulier. De cette façon, vous n'avez pas besoin de lire l'iframe.

  • Placez la page entière dans un iframe qui s'exécute sur le serveur distant. Cela pourrait simplement déplacer le problème, mais si la sortie XML est la dernière étape d'un processus, il est tout à fait possible.

Je suis sûr que vous avez de bonnes raisons pour que le serveur de traitement se trouve sur un domaine différent, mais si ce n'était pas, vous n'auriez pas tous ces problèmes. Peut-être vaut-il la peine de reconsidérer?

Si vous le pouvez, renvoyez une page HTML au lieu du XML.
Dans cette page, vous pouvez utiliser dans une balise SCRIPT la commande: parent.postMessage

Si vous devez supporter d'anciens navigateurs (<IE8 principalement), vous pouvez écrire et lire window.name pour des messages inférieurs à 2Mb.

Les deux techniques vous permettent de passer des données de chaîne entre des images de différents domaines.

Une autre technique consiste à utiliser un setInterval qui appellera à plusieurs reprises le domaine distant à partir de la page parent en utilisant JSONP pour connaître l'état.

En tout cas, vous aurez besoin d'une coopération du domaine distant pour obtenir les données.

L'approche suivante fonctionne dans mon installation (Firefox 3.6):

 <!-- hidden target frame --> <iframe name="load_target" id="load_target" onload="process(this);" src="#" ...> <!-- get data from iframe after load and process them --> <script type="text/javascript"> function process(iframe) { var data = iframe.contentWindow.document.body.innerHTML; // got test data="<xml><a>b</a></xml>" } </script> 

Il fonctionne également sur Chrome, mais il est nécessaire d'exclure un premier appel en charge après le chargement de la page parentale. Cela s'effectue facilement en définissant une variable "globale" qui est testée en process() .

UNE ADDITION

La méthode fonctionne avec un formulaire

 <form action="URL" method="post" enctype="multipart/form-data" target="load_target"> 

Qui est soumis à l' URL . Cette URL doit résider sur le même domaine que la page parent page.html . Si les données d'un REMOTE_URL doivent être téléchargées, l' URL serait un PHP proxy.php sur le propre domaine avec le contenu

 <?php echo file_get_contents("REMOTE_URL"); ?> 

C'est une approche simple – cependant, il est probablement exclu par la condition (2) de la question. Je l'ai ajouté ici pour que ma réponse soit complète.

D'autres approches, compte tenu des iframes seulement, sont discutées par Mahemoff et Georges Auberger .