Прослеживание прогресса XMLHttpRequest и Symfony (CORS)

Я делаю веб-сайт, используя структуру Symfony, на сервере ovh. Когда пользователь меняет страницу, я создаю новое XMLHttpRequest чтобы избежать перезагрузки всей страницы и улучшения пользовательского интерфейса.

Все работает хорошо, но я хочу добавить панель загрузки, а следующая страница загружается асинхронно. К сожалению параметр lengthComputable был ложным. Чтобы пройти эту проблему, я установил заголовок на Symfony3 перед отправкой ответа с помощью строк тезисов:

 $length = strlen($event->getResponse()->getContent()); $event->getResponse()->headers->set('Content-Length', $length); $event->getResponse()->headers->set('Accept-Ranges', "bytes"); $event->getResponse()->sendHeaders(); 

Этот трюк работает на моем локальном сервере разработки, lengthComputable установлен в true, и я могу рассчитать текущий процент нагрузки. Но когда я помещаю все на свой сервер удаленных разработчиков, lengthComputable снова является ложным.

Вот заголовок ответа, используя тезисы четырех строк:

 Accept-Ranges:bytes Accept-Ranges:bytes Cache-Control:no-cache Cache-Control:no-cache Content-Encoding:gzip Content-Length:3100 Content-Length:3100 Content-Type:text/html; charset=UTF-8 Date:Sat, 28 Jan 2017 12:34:08 GMT Server:Apache Set-Cookie:PHPSESSID=***; path=/; HttpOnly Vary:Accept-Encoding 

(И да, некоторые параметры присутствуют два раза)

Я думаю, что это связано с политикой заголовка перекрестного происхождения, но я не могу найти решение.

Я попытался установить другие параметры, такие как

 $responseHeaders->set('Access-Control-Allow-Headers', 'content-type'); 

и даже

 $responseHeaders->set('Access-Control-Allow-Origin', '*'); 

Я следил и пробовал тезисы ссылок

Symfony2. Я не могу установить заголовок Content-Length

Как я могу получить доступ к заголовку Content-Length из запроса Ajax для перекрестного домена?

CORS с заголовками php

РЕДАКТИРОВАТЬ

Я не использую прокси, и мой сервер размещен на OVH.

Вот мой код javascript (ничего исключительного)

 ajax_oReq = new XMLHttpRequest(); ajax_oReq.addEventListener("progress", progress, false); ajax_oReq.addEventListener("load", complete, false); ajax_oReq.addEventListener("error", error, false); ajax_oReq.addEventListener("abort", error, false); params = "ajax=1"; if(url.indexOf('?')!=-1){ params = "&"+params; }else{ params = "?"+params; } ajax_oReq.open("GET", url+params, true); ajax_oReq.send(); 

В моей функции progress(evt) я вижу, что lengthComputable является ложным, регистрируя параметр evt .

В моей complete(evt) функции я evt.target.response в свой соответствующий div .

Я просто хочу добавить, что, когда я использую этот код, я получаю последнее событие прогресса примерно через 500 мс с загруженной длиной, равной общей длине страницы (так что 100% документа, но lengthComputable – false, а total равно 0, поэтому я знаю это только потому, что я смотрю свои заголовки, когда загрузка заканчивается). Но для complete всей функции требуется еще 5 секунд. Я думаю, это потому, что длина контента неизвестна. Во всяком случае, когда я удаляю свои 4 строки кода (первые), этот вопрос исчезает, но у меня всегда нет lengthComputable=true

Спасибо за ваше время !

Поэтому проблема заключалась в том, что mod_deflate Apache сжимал ответ в GZIP, в результате длина была не вычислимой клиентской стороной (потому что Apache отбирает ответ от того, что я прочитал).

Быстрое и грязное решение – отключить сжатие для этого URL-адреса в настройках Apache. Если вы не можете или не хотите, чтобы он был оптимизирован, есть более сложное решение, взятое здесь

 $event->getResponse()->headers->set('x-decompressed-content-length', $length); 

то ваша функция прогресса должна выглядеть примерно так:

 var progress = function (e) { var contentLength; if (e.lengthComputable) { contentLength = e.total; } else { contentLength = e.target.getResponseHeader('x-decompressed-content-length'); } var progress = (e.loaded / contentLength) * 100; }; 

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

Вы также можете попробовать это, взятое отсюда , я позволю вам перевести его на Symfony, потому что я не уверен в том, как вы настраиваете свой контент, но в основном речь идет о том, чтобы вручную загружать данные, чтобы Apache этого не делал.

  // checks if gzip is supported by client $pack = true; if(empty($_SERVER["HTTP_ACCEPT_ENCODING"]) || strpos($_SERVER["HTTP_ACCEPT_ENCODING"], 'gzip') === false) //replace $_SERVER part with SF method { $pack = false; } // if supported, gzips data if($pack) { $replyBody = gzencode($replyBody, 9, FORCE_GZIP); // Watch out 9 is strong compression // Set SF's Response content with gzipped content here header("Content-Encoding: gzip"); // replace that with appropriate SF method } else { // Set SF's Response content with uncompressed content here } // compressed or not, sets the Content-Length header("Content-Length: " . strlen($replyBody)); // replace that with appropriate SF methods 

Так что добавьте первые два, if в вашем контроллере, или где бы вы не установили контент Response и не сохранили свой приемник событий как есть.

Это два наименее «хакерских» решения, которые я мог найти. Кроме того, ваша догадка так же хороша, как и моя, я раньше этого не пробовал.