2016-08-01 5 views
1

Мое приложение должно передать обратно в браузер большой файл, то есть сервер удаленно. В настоящее время файл подается с локального сервера NodeJS.Стрим удаленный файл с PHP и Guzzle

Я использую образ диска VirtualBox размером 25 ГБ, чтобы быть уверенным, что он не сохраняется в памяти во время потоковой передачи. Это соответствующий код, который я борюсь с

require __DIR__ . '/vendor/autoload.php'; 
    use GuzzleHttp\Stream\Stream; 
    use GuzzleHttp\Stream\LimitStream; 

    $client = new \GuzzleHttp\Client(); 
    logger('==== START REQUEST ===='); 
    $res = $client->request('GET', 'http://localhost:3002/', [ 
     'on_headers' => function (\Psr\Http\Message\ResponseInterface $response) use ($res) { 
     $length = $response->getHeaderLine('Content-Length'); 
     logger('Content length is: ' . $length); 
     header('Content-Description: File Transfer'); 
     header('Content-Type: application/octet-stream'); 
     header('Content-Disposition: attachment; filename="testfile.zip"'); 
     header('Expires: 0'); 
     header('Cache-Control: must-revalidate'); 
     header('Pragma: public'); 
     header('Content-Length: ' . $length); 

     } 
    ]); 

    $body = $res->getBody(); 
    $read = 0; 
    while(!$body->eof()) { 
     logger("Reading chunk. " . $read); 
     $chunk = $body->read(8192); 
     $read += strlen($chunk); 
     echo $chunk; 
    } 
    logger('Read ' . $read . ' bytes'); 
    logger("==== END REQUEST ====\n\n"); 

    function logger($string) { 
     $myfile = fopen("log.txt", "a") or die ('Unable to open log file'); 
     fwrite($myfile, "[" . date("d/m/Y H:i:s") . "] " . $string . "\n"); 
     fclose($myfile); 
    } 

Даже если $body = $res->getBody(); должен возвращать поток, он быстро полный диск с данными подкачки, а это означает, что он пытается сохранить в памяти, что перед потоковой передачей обратно в клиент, но это не ожидаемое поведение. Что мне не хватает?

ответ

2

Вы должны указать stream и sink варианты, как это:

$res = $client->request('GET', 'http://localhost:3002/', [ 
    'stream' => true, 
    'sink' => STDOUT, // Default output stream. 
    'on_headers' => ... 
]); 

После этих дополнений вы будете иметь возможность транслировать отклики кусок на кусок, без какого-либо дополнительного кода, чтобы скопировать из потока тела ответа на STDOUT (с echo).

Но обычно вы не хотите этого делать, потому что для каждого активного клиента вам потребуется один процесс PHP (php-fpm или apache mod_php).

Если вы хотите просто хранить секретные файлы, попробуйте использовать «внутреннее перенаправление»: через заголовок X-Accel-Redirect для nginx или X-Sendfile для Apache. Вы получите то же поведение, но с меньшим использованием ресурсов (из-за высокой оптимизации цикла событий в случае nginx). Для деталей конфигурации вы можете ознакомиться с официальной документацией или, конечно, другими вопросами SO (например, this one).

+0

подсказка 'stream => true' просто работала так, как я ожидал! Для наших целей, вероятно, этого достаточно (не так много клиентов, активных в то же время, мы ожидаем), но я буду внимательно читать связанный поток, чтобы я мог попробовать также «более безопасную» альтернативу. В любом случае, удивительный ответ! –

 Смежные вопросы

  • Нет связанных вопросов^_^