2011-07-16 31 views
46

¡Es posible! Lee abajo.


En primer lugar, permítanme usar este diagrama para explicar cómo se pueden lograrla carga de archivos asíncronos:¿Es posible realizar una carga de archivos asíncrona entre dominios?


Lo sentimos. Cerré uno de mis dominios y la imagen ya no está. Aunque fue una imagen realmente bonita. Esto fue antes de descubrir que Stack Overflow permite subir imágenes a través de Imgur.


Como se puede ver, el truco es dejar que la carga HTTP de respuesta en un elemento IFRAME oculto en lugar de la propia página. (Esto se hace configurando la propiedad target del elemento FORM al enviar el FORMULARIO con JavaScript.)

Esto funciona. Sin embargo, el problema que estoy enfrentando es que el script del lado del servidor está en un dominio diferente . El FORM-submit es una solicitud HTTP entre dominios. Ahora, el script del lado del servidor tiene CORS habilitado, lo que le da a mi página web los derechos para leer los datos de respuesta de las solicitudes HTTP hechas desde mi página a esa secuencia de comandos, pero eso solo funciona si recibo la respuesta HTTP a través de Ajax. ergo, JavaScript.

Sin embargo, en este caso, la respuesta se dirige hacia el elemento IFRAME. Y una vez que la respuesta XML llega al IFRAME, su URL será la secuencia de comandos eliminada, p. http://remote-domain.com/script.pl.

Lamentablemente, CORS no cubre este caso (al menos creo que) - No puedo leer el contenido de IFRAME porque su URL no coincide con la URL de la página (dominio diferente). Me sale este error:

Unsafe JavaScript attempt to access frame with URL hxxp://remote-domain.com/script.pl from frame with URL hxxp://my-domain.com/outer.html. Domains, protocols and ports must match.

Y puesto que el contenido del IFRAME es un documento XML, no hay ningún código JavaScript dentro del IFRAME que podría hacer uso de postMessage o algo así.

Así que mi pregunta es: ¿Cómo puedo obtener los contenidos XML del IFRAME?

Como dije anteriormente, puedo recuperar las respuestas HTTP entre dominios directamente (CORS habilitado), pero parece que no puedo leer las respuestas HTTP entre dominios una vez que se cargan en un IFRAME.

Y como si esta pregunta no es suficiente sin solución, que me excluir estas soluciones:

  1. easyXDM y técnicas similares que requieren un punto final en el dominio remoto,

  2. que altera la respuesta XML (para incluir un elemento SCRIPT),

  3. proxy del lado del servidor - Entiendo que podría tener un script del lado del servidor en mi dominio que podría servir como un proxy.

Entonces, aparte de esas dos soluciones, ¿se puede hacer esto?


Se puede hacer !!

Resulta que es posible forjar una solicitud XHR (petición Ajax) que imita un envío de formulario multipart/form-data (que se utiliza en la imagen de arriba para cargar el archivo en el servidor).

El truco es usar el constructor FormData - lea this Mozilla Hacks article para obtener más información.

Esta es la forma de hacerlo:

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

El método anterior se ejecuta un archivo-uplaod asíncrona, lo que equivale a la carga de archivos periódica descrita en la imagen anterior y ha logrado mediante la presentación de esta forma:

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

Como Boss :)

+0

Si no puede editar la respuesta de los servidores remotos, entonces no. Puede usar un truco de intercambio o postmensaje si puede editar el origen del sitio de carga. – William

+0

Si no te importan tanto los navegadores antiguos, puedes usar un método de carga más común en el que subas el archivo a JS y lo publiques a través de AJAX. Si esto parece una buena idea, házmelo saber y lo publicaré como respuesta. –

+0

@Thomas No me interesan los navegadores más antiguos; de hecho, estoy bien incluso si funciona en un solo navegador ':)'. ¿Podrías elaborar un poco más? Me temo que el script del servidor espera un '

' y no estoy seguro si podría crear algo así con JavaScript ... –

Respuesta

9

Simplemente envíe una solicitud XHR entre dominios con los datos del formulario en lugar de enviar el formulario. CORS es solo para el primero.

Si debe hacerlo de la otra manera, negocie con el marco usando postMessage.

And since the contents of the IFRAME is an XML document, there is no JavaScript code inside the IFRAME which could make use of postMessage or something.

¿Cómo te detiene? Incluya un elemento de script en el espacio de nombres HTML o SVG (<script xmlns="http://www.w3.org/1999/xhtml" type="application/ecmascript" src="..."/>) en cualquier parte del XML.

+1

[Leer aquí] (http://jquery.malsup.com/form/#file-upload). No es posible cargar archivos utilizando el objeto 'XMLHttpRequest'. Es por eso que este oculto-IFRAME-hack se introdujo en primer lugar. En cuanto a incluir un elemento SCRIPT dentro de la respuesta XML, es una gran idea. Desafortunadamente, mi requisito es que el dominio remoto no se modifique de ninguna manera (no mencioné esto). Por lo tanto, easyXDM está fuera de cuestión y también modifica la respuesta XML. El objetivo de mi pregunta es averiguar si esto se puede hacer sin modificar el dominio remoto o usar un proxy. –

+2

No, es posible cargar 'Archivos' (que son solo' Blobs') a través de XHR. Todos los navegadores actuales e IE10 soportan la carga de archivos usando W3C File API http://www.w3.org/TR/FileAPI/. Simplemente haga 'xhr.send (file_input.files [0])'. –

+0

Creo que tenemos que esperar 5 años antes de que podamos descartar la solución iframe como alternativa. – BalusC

-1

Si es posible, volver una página HTML en lugar de XML.
En esa página se puede utilizar en una etiqueta SCRIPT el comando: parent.postMessage

Si tiene que soportar los navegadores antiguos (principalmente < IE8), se puede escribir y leer mensajes de window.name por debajo de 2 Mb.

Ambas técnicas le permiten pasar datos de cadena entre tramas de diferentes dominios.

Otra técnica es usar un setInterval que llamará repetidamente al dominio remoto desde la página principal usando JSONP para conocer el estado.

En cualquier caso, necesitará una cooperación del dominio remoto para obtener los datos.

+0

Desafortunadamente no. No puedo modificar la secuencia de comandos del servidor de ninguna manera. –

-1

El siguiente enfoque está trabajando en mi configuración (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> 

Se está trabajando en Chrome, así, pero es necesario para excluir un primer llamado proceso de carga después de la carga de la página principal. Esto se logra fácilmente estableciendo una variable "global" que se prueba en process().

ADEMÁS

El método funciona junto con un formulario

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

que se somete a URL. Este URL necesita residir en el mismo dominio que la página primaria page.html. Si se van a descargar los datos de un REMOTE_URL, entonces URL habría un PHP proxy.php en el propio dominio con el contenido

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

Este es un enfoque simple - sin embargo, es probable que sea excluido por la condición (2) de la pregunta. Lo he agregado aquí para completar mi respuesta.

Otros enfoques, considerando iframes solamente, se discuten en Mahemoff y Georges Auberger.

+0

Te perdiste el punto.El contenido IFRAME entre dominios no se puede leer con JavaScript. Su código solo funciona si la página en el IFRAME y la página que contiene ese IFRAME tienen el mismo dominio. Carga 'http: // google.com' en el IFRAME y prueba tu código; no funcionará. –

+0

@ Šime: gracias por su comentario. He extendido mi respuesta solo para que esté completa y funcione para páginas "remotas". Soy consciente de que este enfoque probablemente esté excluido por su pregunta. – Jiri

+0

Proxying está explícitamente prohibido por la pregunta. – BalusC

0

Creo que no se puede hacer con la forma en que se describe. Normalmente, si tiene problemas de dominio cruzado, puede resolverlo mediante un enfoque JSONp, pero eso solo funciona para las solicitudes GET. Con HTML5, podría enviar binarios con la solicitud GET, pero eso es dudoso.

  • Una solución sería hacer que el servicio web remoto esté disponible localmente mediante el proxy de la solicitud en el servidor web local. Esto causará una carga adicional para su servidor web local, por lo que puedo imaginar que no es factible. Si los archivos son pequeños e infrecuentes, esto funcionará muy bien.

  • Otra solución sería comenzar a sondear el servidor después de haber enviado el archivo. Puede enviar un token y sondear el estado del servidor utilizando JSONp regular. De esta forma no es necesario que lea el iframe.

  • Coloque toda la página en un iframe que se ejecute en el servidor remoto. Esto podría mover el problema, pero si el resultado XML es el paso final en algún proceso, es bastante factible.

estoy seguro de que tiene unas buenas razones para el servidor de procesamiento para estar en un dominio diferente, pero si no fuera no tendría todos estos problemas. Tal vez vale la pena reconsiderar?

+0

# 1 está explícitamente excluido por la pregunta. # 2 y # 3 solo son posibles si la base de código del otro lado está bajo control total. Aparentemente, este no es el caso según el comentario de OP sobre la respuesta de Mic. – BalusC

Cuestiones relacionadas