Потоковая передача больших файлов из внешнего веб-сервиса через PHP - PullRequest
0 голосов
/ 09 декабря 2010

В настоящее время я использую внешние веб-службы SOAP, которые разрешают частичную загрузку / выгрузку двоичных файлов (должны разрешать файлы большего размера).Мне нужно разрешить конечному пользователю загружать файлы через браузер с моим приложением PHP.Обслуживание небольших файлов работает хорошо, но файлы размером более 25 МБ приводят к нехватке памяти на веб-сервере.

Я использую собственный клиент Soap PHP (без поддержки MTOM) и запрашиваю загрузку, отправив форму.В настоящее время создается впечатление, что веб-сервер пытается загрузить весь файл перед выводом чего-либо в браузер (например, приглашение «Загрузить» не отображается до тех пор, пока весь файл не будет обработан с помощью PHP).

Myметод выглядит примерно так (извините, если он неопрятный, я некоторое время решал эту проблему).

public function download()
{
    $file_info_from_ws ... //Assume setup from $_REQUEST params

    //Don't know if these are needed
    gc_enable();
    set_time_limit(0);
    @apache_setenv('no-gzip', 1);
    @ini_set('zlib.output_compression', 0);

    //File Info
    $filesize = $file_info_from_ws->get_filesize();
    $fileid = $file_info_from_ws->get_id();
    $filename = $file_info_from_ws->get_name();
    $offset = 0;
    $chunksize = (1024 * 1024);

    //Clear any previous data
    ob_clean();
    ob_start();

    //Output headers
    header('Content-Type: application/octet-stream');
    header('Content-Length: ' . $filesize);
    header('Content-Transfer-Encoding: binary');
    header('Content-Disposition: attachment; filename="' . $filename . '"');
    header('Accept-Ranges: bytes');

    while($offset < $filesize)
    {
      $chunk = $this->dl_service()->download_chunked_file($fileid, $offset, $chunksize);
      if($chunk)
      {
        //Immediately echo out the stream
        $chunk->render();
        $offset += $chunksize;
        unset($chunk); //Shouldn't this trigger GC?
        ob_flush();
      }
    }
    ob_end_flush();
}

Так что мой главный вопрос: Что лучшеспособ вывода больших двоичных фрагментов из внешних ресурсов (веб-сервисы, БД и т. д.) через PHP для конечного пользователя?Желательно, не убивая слишком много памяти / ЦП.

Также мне интересно следующее:
Почему после первого вывода всплывающее окно «Загрузка» не появляется?
Почему память не освобождается после каждого цикла в методе about?

Ответы [ 2 ]

1 голос
/ 10 декабря 2010

Ну, я чувствую себя глупо. Это оказался просто еще один PHP-изм. Очевидно, хотя я очищал выходной буфер с помощью ob_flush, который (я думал) должен был отправлять заголовки и чанки в браузер, заголовки и выходные данные фактически не сбрасывались в браузер до тех пор, пока сценарий не завершился. *

Даже если сам вывод сбрасывался, вам все равно нужно явно flush записать буферы PHP и веб-сервер обратно на клиент. Невыполнение этого требования приводит к расширению памяти, а приглашение к загрузке не отображается, пока не завершится вся загрузка.

Вот вариант метода работы:

public function download()
{
    $file_info ... //Assume init'ed from WS or DB

    //Allow for long running process
    set_time_limit(0);

    //File Info
    $filesize = $file_info->get_filesize();
    $fileid = $file_info->get_id();
    $filename = $file_info->get_name();
    $offset = 0;
    $chunksize = (1024 * 1024);

    //Clear any previous data
    ob_clean();
    ob_start();

    //Output headers to notify browser it's a download
    header('Content-Type: application/octet-stream');
    header('Content-Length: ' . $filesize);
    header('Content-Disposition: attachment; filename="' . $filename . '"');

    while($offset < $filesize)
    {
      //Retrieve chunk from service
      $chunk = $this->dl_service()->download_chunked_file($fileid, $offset, $chunksize);
      if($chunk)
      {
        //Immediately echo out the stream
        $chunk->render();
        //NOTE: The order of flushing IS IMPORTANT
        //Flush the data to the output buffer
        ob_flush(); 
        //Flush the write buffer directly to the browser
        flush();
        //Cleanup and prepare next request
        $offset += $chunksize;
        unset($chunk);
      }
    }
    //Exit the script immediately to prevent other output from corrupting the file
    exit(0);
}
1 голос
/ 09 декабря 2010

http://php.net/manual/en/function.fpassthru.php

Это может помочь.Это также может изменить способ, которым вы хотите делать все.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...