web-dev-qa-db-fra.com

Est-il possible d'effectuer un téléchargement de fichiers interdomaine asynchrone?

C'est possible! Lire ci-dessous.


Tout d’abord, permettez-moi d’utiliser ce diagramme pour expliquer comment il est possible de télécharger des fichiers asynchrones:


Désolé. J'ai fermé l'un de mes domaines et l'image est maintenant disparue. C'était une image vraiment sympa, cependant. C'était avant que je découvre que Stack Overflow permet de télécharger des images via Imgur. 


Comme vous pouvez le constater, le truc consiste à laisser la réponse HTTP se charger dans un élément IFRAME masqué au lieu de la page elle-même. (Pour ce faire, définissez 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 est sur un domaine différent. FORM-submit est une requête HTTP interdomaine. Maintenant, le script côté serveur a CORS activé, ce qui donne à ma page Web le droit de lire les données de réponse des requêtes HTTP effectuées depuis ma page vers ce script - mais cela ne fonctionne que 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 aura atterri dans IFRAME, son URL sera le script de suppression - par exemple. http://remote-domain.com/script.pl.

Malheureusement, CORS ne couvre pas ce cas (du moins je pense) - je ne suis pas en mesure de 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:

Tentative JavaScript non sécurisée d'accéder au cadre avec l'URL hxxp: //remote-domain.com/script.pl du cadre avec l'URL hxxp: //mon-domain.com/outer.html. Les domaines, les protocoles et les ports doivent rencontre.

Et comme le contenu de l'IFRAME est un document XML, il n'y a pas de code JavaScript à l'intérieur de l'IFRAME qui pourrait utiliser postMessage ou quelque chose d'autre.

Ma question est donc la suivante: Comment puis-je obtenir le contenu XML de l'IFRAME?

Comme je l'ai dit plus haut, je suis en mesure de récupérer directement les réponses HTTP entre domaines (CORS activé), mais il semble que je ne puisse pas lire les réponses HTTP entre domaines une fois qu'elles ont été chargées dans un IFRAME.

Et comme si cette question n’était pas assez insoluble, laissez-moi exclure ces solutions:

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

  2. modifier 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 demande XHR (demande Ajax) qui imite une soumission de multipart/form-data FORM (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 de 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 téléchargement de fichier standard 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 :)

47
Šime Vidas

Envoyez simplement une demande XHR interdomaine avec les données du formulaire au lieu de le soumettre. CORS est seulement pour les anciens.

Si vous devez le faire dans l'autre sens, négociez avec le cadre à l'aide de postMessage.

Et comme le contenu de l'IFRAME est un document XML, il n'y a pas de code JavaScript à l'intérieur de l'IFRAME qui pourrait utiliser postMessage ou quelque chose d'autre.

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

9
Eli Grey

Je pense que cela ne peut pas être fait avec la façon dont vous décrivez. Normalement, si vous rencontrez des problèmes inter-domaines, vous pouvez le résoudre par une approche JSONp, mais cela ne fonctionne que pour les requêtes GET. Avec HTML5, vous pouvez potentiellement envoyer du binaire avec la requête GET, mais ce n'est pas très grave.

  • Une solution serait de rendre le service Web distant disponible localement en envoyant la requête par proxy sur le serveur Web local. Cela entraînera une charge supplémentaire pour votre serveur Web local, donc je peux imaginer que cela est impossible. Si les fichiers sont petits et peu fréquents, cela fera l'affaire.

  • Une autre solution consiste à interroger le serveur une fois le fichier envoyé. Vous pouvez envoyer un jeton et interroger l'état du serveur à l'aide de JSONp standard. 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 déplacer le problème, mais si la sortie XML est la dernière étape d'un processus, c'est tout à fait faisable.

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

0
Halcyon