Upload progress средствами PHP 5.4

В текущее время существует множество вариантов определения прогресса загрузки файла: как с помощью клиентских технологий, так и с помощью серверных. Примером клиентских технологий служат swfupload с использованием Flash, примером серверных — nginx_uploadprogress_module.
Грядущий релиз PHP преподносит нам подарок в виде родного инструмента для определения прогресса закачки файла.
Разберемся с ним.

Пока еще PHP находится в стадии RC, но в скором времени он появится на выделенных серверах, VDS и особо смелых хостингах. В любом случае в неопределенном будущем PHP 5.4 станет основной ветвью, а PHP 5.3 объявят deprecated.
Начнем с формы:

<form action="" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="<?=ini_get("session.upload_progress.name")?>"
            value="uniqueValue" /><br />
    <input type="file" name="file" /><br />
    <input type="submit" />
</form>


Единственное примечательное в ней место — скрытое поле с именем PHP_SESSION_UPLOAD_PROGRESS(которое может меняться, поэтому определено здесь через функцию). И любым уникальным значением.

Поместим форму в iframe на целевую страницу.
И поместим туда же наш скрипт для определения процента загрузки:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script>
    $(function() {
        setInterval(function() {
            $.get('?ajax', function(data) {
                $('#ajax').html(data);
            });
        }, 500);
    });
</script>
<div id="ajax"></div>

Для асинхронного запроса я применил библиотеку jQuery для уменьшения кода и улучшения его читаемости.
В данном случае все, что мы получаем из асинхронных запросов, я вывожу в блок с идентификатором ajax.

И последняя часть — серверный обработчик асинхронного запроса:

if (isset($_SESSION["upload_progress_uniqueValue"])) {
    $progress = $_SESSION["upload_progress_uniqueValue"];
    $percent = round(100 * $progress['bytes_processed'] / $progress['content_length']);
    echo "Upload progress: $percent%<br /><pre>" . print_r($progress, 1) . '</pre>';
} else {
    echo 'no uploading';
}

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

Помещаем это все на одну страницу, код небольшой(полстраницы) и поэтому я выложу его прямо здесь:
<?php
session_start();
$uploadName = 'test'; // уникальное имя
if (isset($_GET['ajax'])) { // асинхронный запрос
    if (isset($_SESSION["upload_progress_$uploadName"])) { // файл загружается в данный момент
        $progress = $_SESSION["upload_progress_$uploadName"];
        $percent = round(100 * $progress['bytes_processed'] / $progress['content_length']);
        echo "Upload progress: $percent%<br /><pre>" . print_r($progress, 1) . '</pre>';
    } else {
        echo 'no uploading';
    }
    exit;
} elseif (isset($_GET['frame'])) { // покажем фрейм ?>
    <form action="" method="POST" enctype="multipart/form-data">
        <input type="hidden" name="<?=ini_get("session.upload_progress.name")?>"
                value="<?=$uploadName?>" /><br />
        <input type="file" name="file" /><br />
        <input type="submit" />
    </form>
<?php } else { ?>
    <iframe src="?frame" height="100" width="500"></iframe>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <script>
        $(function() {
            setInterval(function() { // вызываем периодически
                $.get('?ajax', function(data) { // и принимаем значения
                    $('#ajax').html(data); // записывая их на страницу
                });
            }, 500);
        });
    </script>
    <div id="ajax"></div>
<?php }

Верстка невалидная — специально сокращена для примера настолько, насколько возможно.

Скриншот для наглядности:


Статья предоставляет базовые сведения, которые можно найти и в документации, но в виду того, что в текущей актуальной документации этого нет, а в книгах — тем более, я посчитал, что она будет полезна для PHP-разработчиков.

Похожие публикации

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 73

    –19
    раньше был extension, теперь добавили в основной код. зачем? непонятно :(
    Мое мнение, лучше делать через html5, swf или silverlight…
      +19
      В основной код добавили, чтобы работало везде, т.к. данный функционал будет полезен для продуктов любого уровня.
      HTML5-решение не работает на IE9.
      swf-решение неудобно для многих(нетбуки, мобильные устройства) и их процент постоянно растет.

      А серверное решение работает всегда.
        –12
        Серверное решение не удобно, так как создает лишние соединения с сервером, php поднимает сессию, и лезит смотреть статус загрузки. В свое время отказался от uploadprogress из-за этого, и сделал вариант с swfupload и обычным, если флэша нет.
          +14
          У вас сервер на кпк стоит что ли?
            –3
            У меня был бесплатный хостинг картинок, сервак забивал порядка до 60мбит в днем. (в месяц было 3-4Тб)
            Посещаемость: 30-40+ тыс уников, 120-160+ просмотров
            В день заливалось порядка 10 тыс фотографий
            За год, было занято порядка 500Гб картинками

            Возможно у меня была плоха оптимизация, но отключив uploadprogress я получил меньше запросов к ПХП, и меньший расход памяти
              +4
              Задача специфическая, напрашивается решение вроде nginx_uploadprogress_module — не стандартное, но легковесное.
                0
                Если что, nginx_uploadprogress_module тоже нужно проверять регулярно, так что поменяете шило на мыло, разве что пхп трогать не будете =)
                  –1
                  Сравните отдачу файла через nginx и readfile(), примените аналогию и сделайте вывод.
                    –1
                    Ну вы сравнили конечно отдачу и закачку…
                    +1
                    Уверяю, замена php на nginx это далеко не шило на мыло. Потребление ресурсов первого и второго просто несопоставимы, поэтому тут даже сравнивать нечего.
                      0
                      Вы решили повторить камент человека выше? В этом нет смысла, я понял все с первого раза.
                        0
                        Нет, не решил. Просто с момента открытие страницы и начала чтения каментов и до момент написания своего камента проходит время за которое может появиться масса новых каментов. А автоподгрузка каментов или вывод перед постингом появившихся каментов на хабре нет и, как я понимаю, не предвидится пока.
                  0
                  swfupload не делает запросы к серверу во время показа загрузки, он сам все считает на клиенте, зря отказались :) Вам просто «показалось» что срабо работать быстрей
                    +2
                    где я написал что отказался от swfupload? я отказался от extension php uploadprogress, который они включили в 5.4 по дефолту.
                      0
                      как я уже ниже писал — в PHP 5.4. это не внедренное расширение.

                      Хук был еще с 5.2 для работы с загрузкой, теперь же просто добавили возможность считывать информацию из сессии, без применения сторонних расширений.
                        0
                        А как называлось расширение, с помощью которого можно было реализовать upoload progress в предыдущих версиях PHP?
            0
            Насчет нетбуков не очень понятно, чем неудобно SWF-решение, ведь Windows — он и в Африке Windows, и флешовый плагин можно поставить точно так же, как и на десктоп. Что касается мобильных устройств, то да, но с одной оговоркой — на мобильных телефонах и прочих малофункциональных/неудобных девайсах вряд ли будут грузить файлы. Однако тут встает такой вопрос — а насколько хорошо поддерживают те же мобильные устройства, с которых будут загружаться файлы (если честно, вообще-то я плохо представляю серьезную работу на планшетнике с последующей загрузкой файлов на сайт, но, в принципе, такое возможно — видел, как девушка в метро на планшете коллаж из фоток делала), JS/Ajax (и заодно SWF и HTML5)?
              –1
              А если взять в рассмотрение Chrome-OS например? Флеша/SL — нет, зато JS/Ajax работает на ура.
                +1
                Как это нет флеша? Есть флеш, проверьте перед тем как утверждать :)
                  0
                  Утверждение спорное, потому как Chome OS по сути — браузер
                  0
                  Хохо — а как же «запостить фоточку с айфончика вконтактик»?
                    +1
                    На айфончике кнопка «обзор» вообще не работает
                +4
                Не понял за что наминусовали. Человек всё правильно написал.
                Пользователи IE вообще идут лесом:) Для них можно показывать просто картинку процесса загрузки, пусть пойдут поставят нормальный браузер, если хотят плюшки.

                Html5 + Flash решение намного легче чем серверное и для хайлоада разница будет колосальная по нагрузке.
                  +2
                  Поддерживаю. В то время как одни как только могут пытаются сократить кол-во обращений к серверу, другие наоборот предлагают его увеличить из-за такой ничтожной задачи.

                  Это как-то вообще нелогично: ведь клиент прекрасно знает, сколько он отправил уже байт, зачем мучать сервер то? Да, все понятно, есть еще IE, но даже там это можно обойти при большом желании (swf, и т.д). Да и IE10, в котором это будет, уже не за горой. Скоро получится так, что все браузеры будут поддерживать это, а в PHP так и останется уйма лишнего функционала, увеличивающего размер и влияющего на быстродействие.

                  P.S. Да, уж если непременно хочется кому-то через сервер, то расширения было бы вполне достаточно.
                +1
                Больше всего волнует один вопрос: как это работате в случае загрузке нескольких файлов?
                  +4
                  Добавьте поле: <input type=«file» name=«file2» /><br /> и посмотрите, что пишет print_r
                  –11
                  не поздновато ли?
                    +11
                    Ну да, кто ж в наше время-то файлы на сервер загружает
                      –19
                      делать это было нужно лет 5 назад, сейчас уже сделать это проще и быстрее через flash и/или html5, отсталые вы мои
                        +2
                        Если есть способ сделать то же самое без всяких надстроек над браузером, то логичнее выбрать именно этот вариант.
                          0
                          html5 надстройка, да.
                          минусуйте минусуйте :)
                            0
                            HTML 5 не доступен в некоторых браузерах, которые все еще используют. Если нужен более широкий охват аудитории, то, опять же, логичнее использовать серверное решение.
                    0
                    Я правильно понимаю, что взято вот это расширение — pecl.php.net/package/uploadprogress и теперь оно доступно в 5.4. как часть ядра?

                    Если да, то это просто замечательная новость, данным расширением пользуюсь уже года 3-4.
                      0
                      ага, вижу немного не так:

                      20 Jun 2011, PHP 5.4.0 Alpha 1
                      . Added support for storing upload progress feedback in session data. (Arnaud)
                        0
                        Скажите, где можно почитать подробнее, можно ли использовать это расширение pecl на обычном виртуальном хостинге у среднестатистического провайдера. Если да, то где почитать об этом подробнее? Спасибо!
                          0
                          если у вас VPS/VDS то расширение ставится довольно просто — ставите php5-dev и дальше pecl install uploadprogress

                          > pecl search uploadprogress

                          Retrieving data...0%
                          MATCHED PACKAGES, CHANNEL PECL.PHP.NET:
                          =======================================
                          PACKAGE STABLE/(LATEST) LOCAL
                          uploadprogress 1.0.3.1 (stable) An extension to track progress of a file upload.
                            0
                            На обычном виртуальном хостинге это можно использовать если только сам провайдер готов установить такое расширение. Но из-за одного клиента с ~100-200 руб./мес. ни кто возиться с этим не станет. Таким одиночкам скорее уж предложат VPS/VDS.
                              0
                              Спасибо! Я плачу хостеру в месяц 400 руб. Все чаще задумываюсь о VPS/VDS, если честно.
                                +1
                                Не рекламы ради. За эти деньги можно взять тут fastvps.ru/vds/ витуалку OVZ-3. У меня даже на меньших ресурсах (раньше конфигурация на этих тарифах была скромнее) крутилось что-то около семи сайтов на WP, Joomla и паре самописок. Крутилось целый год без сбоев и хаброэффектов пока не переехал на другой тариф. Так что рекомендую. Но нужно понимать, что требуется приложение рук к конкурированию nginx+php (можно конечно взять и ISP панель, но конфиги она генерит не самые оптимальные, да и ручное вмешательство все же периодически требуется).
                                  +1
                                  Докиньте 100 рублей и вы получите прекрасное облако на scalaxy.

                                  Раньше было за 300 на selectel, но сейчас они клиентов не принимают.

                                  Минус только один: навык администрирования linux сервера обязателен. Isp manager — зло.

                                    0
                                    Еще раз благодарю. Внимательно изучил вопрос и понял, что пока мой удел — стандартные услуги провайдера. И учить матчасть :-)
                                    0
                                    У хетцнера за ~10USD есть более чем сносные VPS
                              +2
                              Не очень понял, зачем нужен iframe?
                              А так статья полезная, никогда не нравилось решение с swf.
                                –4
                                Хороший вопрос.
                                В хроме без ифрейма не работает — начиная с успешного сабмита в нем не работают скрипты. В опере работает.
                                Остальные браузеры не проверял, но с ифреймом должно везде работать
                                  +3
                                  Во-первых во фрейм грузится форма аплоада, во-вторых ифрейм нужен для того чтобы страница не перезагружалась при аплоаде. при нажатии на кнопку «отправить» браузер будет загружать файл на сервер, яваскрипт аякс запросом будет контролировать процесс загрузки и выводить в , а по окончании содержимое ифрейма будет перезагружено, а сама страница с выведеной на ней информацией о прогрессе останет
                                    0
                                    прошу прощения
                                    яваскрипт аякс запросом будет контролировать процесс загрузки и выводить в
                                    <div id="ajax"></div>
                                  0
                                  Спасибо, ждем релиза 5.4.
                                    0
                                    Пожалуйста, оформите код правильно — с подсветкой синтаксиса
                                      –1
                                      А я раньше больше ориентировался на статус-бар загрузки в левом нижнем углу хрома.
                                        0
                                        А я со времен ослика привык, что он погоду показывает.
                                          0
                                          В Опере, кстати, тоже можно включить панельку внизу и там показывает прогресс отправки запроса на сервер.
                                          +2
                                          Для подсветки PHP используйте
                                          <?php echo "hello"; ?>
                                          .

                                          А так, спасибо за статью.
                                            +2
                                            извеняюсь <source lang="php"><?php echo "hello"; ?></source>.
                                              +6
                                              Ага. А то выглядело как в анекдоте про фокусника:
                                              — Дяденька, а как у вас получаете заставить зайца исчезнуть?
                                              — Блин, как вы меня достали такими вопросами! Просто берёшь и делаешь так! — делает два пасса руками и заяц исчезает.
                                              — А, понял, спасибо…
                                            +1
                                            спс за статью, для мелких проектов пригодиться, но при использовании nginx есть 2 неоспоримых плюса:
                                            1. не мучается php (а он как не крути медленный)
                                            2. нет ограничения на длину файла (у php ограничение равно максимальному значению int, так как лимит именно в таком виде хранится, при этом всегда думал что на 64битной архитектуре будет больше, а все равно те же ~2гб)
                                            а еще есть неоспоримый минус — не везде nginx есть =)
                                              0
                                              Мелочь конечно, но хотелось бы отдельную ф-цию для ini_get(«session.upload_progress.name»)
                                                +16
                                                function get_session_upload_progress_name_from_php_configuration() {
                                                  return  ini_get("session.upload_progress.name");
                                                }
                                                +2
                                                Статья предоставляет базовые сведения, которые можно найти и в документации, но в виду того, что в текущей актуальной документации этого нет, а в книгах — тем более, я посчитал, что она будет полезна для PHP-разработчиков.

                                                Я думаю, что PHP разработчики Хабра, а также миллион других пользователей официального сайта PHP были бы благодарны, если бы эту информацию вы бы разместили ещё и в официальной документации. А если на английском, то вообще бы цены не было для мирового сообщества разработчиков.
                                                ИМХО, там её и найдут и увидят в тысячи раз больше людей…
                                                0
                                                А я раньше писал progresses bar загрузки на perl'e. Вообще рад этому обновлению.
                                                  0
                                                  А пример для наглядности можно?
                                                  0
                                                  Блин вот сейчас бы препода носом потыкать.
                                                  Он мне 3 года назад говорил «PHP отстой, он загнется через 3 года и НИКТО не будет писать на нем.»

                                                  PHP очень радует своими обновлениями.
                                                    0
                                                    Никогда не слушай других, прислушивайся и делай анализ сказаного.
                                                      0
                                                      Ну я так и поступил.
                                                    0
                                                    С слабо доработать так чтобы в случае обрыва можно было бы продолжить заливку файла с того места где оборавалось?
                                                      0
                                                      примером серверных — nginx_uploadprogress_module.
                                                      Грядущий релиз PHP преподносит нам подарок в виде родного инструмента для определения прогресса закачки файла.
                                                      Как предупреждение: если есть возможность, то загрузку файлов надо делать средствами сервера, а не средствами РНР, в этом случае нагрузка будет значительно меньше.
                                                        0
                                                        У этих инструментов разное предназначение. Иногда нужно всегда работающее массовое решение, иногда — производительное.
                                                        +1
                                                        Зачем сравнивать это с nginx_uploadprogress_module, если при наличии nginx в качестве реверс-прокси предложенное решение всё равно работать не будет? Реверс-прокси плюнет в бекэнд запросом, когда он полностью получен, то есть с точки зрения php файл загрузится чуть ли не мгновенно.

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

                                                        Чтобы не холиварить: хорошо, что такая возможность появилась. Плохо, что возможность её реального применения под большим вопросом. А сравнение с nginx_uploadprogress_module вообще, имхо, не совсем корректно потому что это, по сути, взаимоисключающие явления.
                                                          0
                                                          Обратите внимание, этотметож не работает под FastCGI!

                                                          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                          Самое читаемое