Czy możliwe jest asynchroniczne przesyłanie plików między domenami?

To możliwe! Przeczytaj poniżej.


Po pierwsze, pozwól mi użyć tego diagramu, aby wyjaśnić, w jaki sposób można uzyskać asynchroniczne przesyłanie plików :


Przepraszam. Wyłączyłem jedną z moich domen i obraz zniknął. To był naprawdę ładny obraz. To było zanim dowiedziałem się, że przepełnienie stosu umożliwia przesyłanie obrazów przez Imgur.


Jak widzisz, sztuczka polega na tym, aby pozwolić odpowiedzi HTTP załadować ukryty element IFRAME zamiast samej strony. (Odbywa się to poprzez ustawienie właściwości target elementu formularza podczas przesyłania formularza za pomocą JavaScript.)

To działa. Jednak problem, z którym się borykam, polega na tym, że skrypt po stronie serwera znajduje się na innej domenie. Formularz-submit jest cross-domain HTTP-request. Teraz skrypt po stronie serwera ma włączone CORS, który daje mojej stronie internetowej prawa do odczytu danych odpowiedzi HTTP-żądania wykonane z mojej strony do tego skryptu - ale to działa tylko wtedy, gdy otrzymam Http-odpowiedź poprzez Ajax, ergo, JavaScript.

Jednak w tym przypadku odpowiedź jest skierowana w stronę elementu IFRAME. A gdy odpowiedź XML trafi do IFRAME, jej URL będzie skryptem Usuń-np. http://remote-domain.com/script.pl.

Niestety, CORS nie obejmuje tego przypadku (przynajmniej tak mi się wydaje) - nie jestem w stanie odczytać zawartości IFRAME, ponieważ jego adres URL nie pasuje do adresu URL strony (Inna domena). I get this error:

Unsafe JavaScript attempt to ramka dostępu z adresem URL hxxp://remote-domain.com/script.pl from frame with URL hxxp://my-domain.com/outer.html. domeny, protokoły i porty muszą mecz.

A ponieważ zawartość ramki IFRAME jest dokumentem XML, nie ma kodu JavaScript wewnątrz ramki IFRAME, który mógłby użyć postMessage lub czegoś takiego.

Więc moje pytanie brzmi: Jak mogę pobrać zawartość XML z IFRAME?

Jak powiedziałem powyżej, jestem w stanie pobrać odpowiedzi HTTP między domenami bezpośrednio (CORS włączone), ale wydaje się, że nie jestem w stanie odczytać odpowiedzi HTTP między domenami po załadowaniu do IFRAME.

I jakby to pytanie nie było wystarczająco nierozwiązywalne, pozwól mi wykluczyć te rozwiązania:

  1. EasyXDM i podobne techniki, które wymagają punktu końcowego na odległej domenie,

  2. Zmiana odpowiedzi XML (w celu włączenia elementu skryptu),

  3. Proxy po stronie serwera-rozumiem, że mogę mieć skrypt po stronie serwera na mojej domenie, który może służyć jako proxy.

Więc, poza tymi dwoma rozwiązaniami, czy można to zrobić?


Można to zrobić!!

Okazuje się, że możliwe jest sfałszowanie XHR-request (Ajax-request), które naśladuje formularz submit (który jest używany na powyższym obrazku do przesłania pliku na serwer).

Sztuką jest użycie FormData konstruktora-przeczytaj Ten artykuł Mozilla Hacks aby dowiedzieć się więcej informacje.

Tak to się robi:

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

Powyższa metoda wykonuje asynchroniczny plik-uplaod, który jest odpowiednikiem zwykłego pliku-upload opisanego na powyższym obrazku i uzyskanego przez przesłanie tego formularza:

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

Jak szef :)

Author: Šime Vidas, 2011-07-16

4 answers

Po prostu Wyślij zapytanie XHR z danymi z formularza zamiast wysyłać formularz. CORS jest tylko dla tych pierwszych.

Jeśli musisz zrobić to w drugą stronę, negocjuj z ramką za pomocą postMessage.

A ponieważ zawartość ramki IFRAME jest dokumentem XML, nie ma kodu JavaScript wewnątrz ramki IFRAME, który mógłby użyć postMessage lub czegoś takiego.

Jak to cię powstrzymuje? Dołączenie elementu skryptu w przestrzeni nazw HTML lub SVG (<script xmlns="http://www.w3.org/1999/xhtml" type="application/ecmascript" src="..."/>) gdziekolwiek w XML.
 9
Author: Eli Grey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-06 00:02:36

Myślę, że nie da się tego zrobić w sposób, w jaki opisujesz. Zwykle, jeśli masz problemy między domenami, możesz je rozwiązać za pomocą podejścia JSONp, ale działa to tylko dla żądań GET. Z HTML5 potencjalnie można wysłać binarny z żądaniem GET, ale to jest po prostu niepewne.

  • Rozwiązaniem byłoby udostępnienie zdalnej usługi internetowej lokalnie poprzez proxy żądania na lokalnym serwerze internetowym. Spowoduje to dodatkowe obciążenie lokalnego serwera WWW, więc mogę sobie wyobrazić, że jest niemożliwe. Jeśli pliki są małe i rzadkie, to będzie dobrze.

  • Innym rozwiązaniem byłoby rozpoczęcie ankiety na serwerze po wysłaniu pliku. Możesz wysłać token i sprawdzić status serwera za pomocą zwykłego JSONp. W ten sposób nie trzeba czytać z iframe.

  • Umieść całą stronę w ramce iframe, która działa na zdalnym serwerze. Może to po prostu przesunąć problem, ale jeśli wyjście XML jest ostatnim krokiem w jakimś procesie, to całkiem wykonalne.

Jestem pewien, że masz dobre powody, aby serwer przetwarzania był na innej domenie, ale gdyby nie było, nie miałbyś tych wszystkich problemów. Może warto to przemyśleć?

 0
Author: Halcyon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-07 00:06:28

Jeśli możesz, zwróć stronę HTML zamiast XML.
na tej stronie możesz użyć w SCRIPT tagu polecenia:parent.postMessage

Jeśli musisz obsługiwać starsze przeglądarki (główniewindow.name dla wiadomości poniżej 2MB.

Obie techniki pozwalają na przekazywanie danych tekstowych pomiędzy ramkami różnych domen.

Inną techniką jest użycie setInterval, która będzie wywoływać wielokrotnie zdalną domenę ze strony nadrzędnej za pomocą JSONP , aby wiedzieć status.

W każdym przypadku, będziesz potrzebował współpracy ze zdalnej domeny, aby uzyskać dane.

 -1
Author: Mic,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-16 19:24:43

W mojej konfiguracji (Firefox 3.6) działa następujące podejście:

<!-- 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>

Działa również w Chrome, ale konieczne jest wykluczenie pierwszego wywołania onload po załadowaniu strony nadrzędnej. Można to łatwo osiągnąć, ustawiając zmienną "globalną", która jest testowana w process().

Dodatek

Metoda współpracuje z formą

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

, która jest przekazywana URL. Ta URL musi znajdować się w tej samej domenie co Strona nadrzędna page.html. Jeśli dane z REMOTE_URL mają być pobrane, to URL będzie PHP proxy.php na własnej domenie z zawartością

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

Jest to proste podejście - jednak prawdopodobnie jest wykluczone przez warunek (2) pytania. Dodałem go tutaj, aby moja odpowiedź była kompletna.

Inne podejścia, biorąc pod uwagę tylko ramki iFrame, są omawiane przez Mahemoff I Georges Auberger .

 -1
Author: Jiri Kriz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-06 12:41:12