Подтвердить что ты не робот

Возможно ли выполнить асинхронную загрузку файлов по нескольким доменам?

Это возможно! Читайте ниже.


Прежде всего, позвольте мне использовать эту диаграмму, чтобы объяснить, как можно добиться асинхронной загрузки файлов:


К сожалению. Я закрыл один из моих доменов, и изображение исчезло. Это был действительно хороший образ. Это было до того, как я узнал, что Qaru позволяет загружать изображения через Imgur.


Как вы можете видеть, трюк заключается в том, чтобы загрузить HTTP-ответ в скрытый элемент IFRAME вместо самой страницы. (Это делается установкой свойства target элемента FORM при отправке FORM с помощью JavaScript.)

Это работает. Однако проблема, с которой я сталкиваюсь, заключается в том, что серверная сторона script находится в другом домене. FORM-submit - это междоменный HTTP-запрос. Теперь серверная сторона script имеет включенную CORS, которая дает моей веб-странице права на чтение данных ответа HTTP-запросов, сделанных с моей страницы, на этот script - но это работает только в том случае, если я получаю HTTP- ответ через Ajax, ergo, JavaScript.

Однако, в этом случае ответ направлен на элемент IFRAME. И как только XML-ответ попадет в IFRAME, его URL-адрес будет удаляться script - например. http://remote-domain.com/script.pl.

К сожалению, CORS не охватывает этот случай (по крайней мере, я думаю). Я не могу прочитать содержимое IFRAME, так как его URL-адрес не соответствует URL-адресу страницы (другой домен). Я получаю эту ошибку:

Небезопасная попытка JavaScript для доступа к фрейму с URL-адресом hxxp://remote-domain.com/ script.pl из фрейма с URL-адресом hxxp://my-domain.com/outer.html. Домены, протоколы и порты должны матч.

И поскольку содержимое IFRAME является XML-документом, внутри IFRAME нет кода JavaScript, который мог бы использовать postMessage или что-то в этом роде.

Итак, мой вопрос: Как я могу получить содержимое XML из IFRAME?

Как я уже говорил выше, я могу напрямую получить междоменные HTTP-ответы (CORS включен), но кажется, что я не могу читать междоменные HTTP-ответы после их загрузки в IFRAME.

И как если бы этот вопрос не был достаточно неразрешим, позвольте мне исключить эти решения:

  • easyXDM и аналогичные методы, которые требуют конечной точки в удаленном домене,

  • изменение ответа XML (включение элемента script),

  • серверный прокси - я понимаю, что у меня может быть серверный script в моем домене, который может служить прокси.

Итак, не считая этих двух решений, можно ли это сделать?


Это можно сделать!!

Оказывается, можно подделать XHR-запрос (Ajax-запрос), который имитирует submit multipart/form-data FORM submit (который используется на изображении выше для загрузки файла на сервер).

Хитрость заключается в использовании конструктора FormData - прочитайте эту статью Mozilla Hacks для получения дополнительной информации.

Вот как вы это делаете:

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

Вышеупомянутый метод выполняет асинхронный файл-uplaod, который эквивалентен обычной загрузке файла, описанной на изображении выше, и достигается путем отправки этой формы:

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

Как босс:)

4b9b3361

Ответ 1

Просто отправьте кросс-доменный запрос XHR с данными из формы вместо отправки формы. CORS только для первого.

Если вы должны сделать это другим способом, перейдите к кадру с помощью postMessage.

И поскольку содержимое IFRAME является XML-документом, внутри IFRAME не существует кода JavaScript, который мог бы использовать postMessage или что-то в этом роде.

Как это останавливает вас? Включите элемент script в пространстве имен HTML или SVG (<script xmlns="http://www.w3.org/1999/xhtml" type="application/ecmascript" src="..."/>) в любом месте XML.

Ответ 2

Я думаю, что это невозможно сделать с тем, как вы описываете. Обычно, если у вас есть проблемы с перекрестным доменом, вы можете решить его с помощью подхода JSONp, но это работает только для запросов GET. С HTML5 вы можете отправить двоичный код с помощью запроса GET, но это просто iffy.

  • Решение состоит в том, чтобы сделать удаленный веб-сервис доступным локально, проксируя запрос на локальном веб-сервере. Это вызовет дополнительную нагрузку для вашего локального веб-сервера, поэтому я могу себе представить, что это невозможно. Если файлы маленькие и нечастые, это будет хорошо.

  • Еще одно решение - начать опрос сервера после того, как вы отправите файл. Вы можете отправить маркер и опросить статус сервера, используя обычный JSONp. Таким образом, вам не нужно читать из iframe.

  • Поместите всю страницу в iframe, которая выполняется на удаленном сервере. Это может просто переместить проблему, но если выход XML является последним шагом в каком-то процессе, это вполне возможно.

Я уверен, что у вас есть веские причины для того, чтобы сервер обработки находился в другом домене, но если бы не вы, у вас не было бы всех этих проблем. Возможно, стоит пересмотреть?

Ответ 3

Если вы можете, верните HTML-страницу вместо XML.
На этой странице вы можете использовать в теге SCRIPT команду: parent.postMessage

Если вам нужно поддерживать старые браузеры (в основном, IE8), вы можете писать и читать window.name для сообщений ниже 2Mb.

Оба метода позволяют передавать строковые данные между кадрами разных доменов.

Другим методом является использование setInterval, который будет многократно вызывать удаленный домен с родительской страницы, используя JSONP, чтобы узнать статус.

В любом случае вам понадобится сотрудничество с удаленным доменом для получения данных.

Ответ 4

В моей настройке (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>

Он также работает в Chrome, но необходимо исключить первый вызов onload после загрузки родительской страницы. Это легко осуществить, установив "глобальную" переменную, которая протестирована в process().

ДОБАВЛЕНИЕ

Метод работает вместе с формой

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

который отправляется на URL. Этот URL должен находиться в том же домене, что и родительская страница page.html. Если данные из REMOTE_URL должны быть загружены, то URL будет PHP proxy.php в собственном домене с содержимым

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

Это простой подход, однако он, вероятно, исключается из условия (2) вопроса. Я добавил его здесь, чтобы закончить мой ответ.

Другие подходы, только с учетом iframes, обсуждаются Mahemoff и Georges Auberger.