Servindo arquivos grandes com PHP

Então eu estou tentando atender arquivos grandes por meio de um script PHP, eles não estão em um diretório acessível na web, então esta é a melhor maneira que eu posso imaginar para fornecer access a eles.

A única maneira que eu poderia pensar fora do morcego para servir este arquivo é carregando na memory (fopen, fread, ect.), Configurando os dados do header para o tipo MIME apropriado e, em seguida, apenas ecoando todo o conteúdo do arquivo.

O problema com isso é que eu tenho que carregar esses arquivos de 700 MB na memory de uma só vez, e manter tudo até que o download seja concluído. Seria bom se eu pudesse transmitir nas partes que eu preciso enquanto estão baixando.

Alguma ideia?

Você não precisa ler o todo – basta inserir um loop, lendo-o, digamos, pedaços de 32Kb e enviando-o como saída. Melhor ainda, use fpassthru o que faz a mesma coisa para você ….

$name = 'mybigfile.zip'; $fp = fopen($name, 'rb'); // send the right headers header("Content-Type: application/zip"); header("Content-Length: " . filesize($name)); // dump the file and stop the script fpassthru($fp); exit; 

ainda menos linhas se você usar readfile , o que não precisa da chamada fopen …

 $name = 'mybigfile.zip'; // send the right headers header("Content-Type: application/zip"); header("Content-Length: " . filesize($name)); // dump the file and stop the script readfile($name); exit; 

Se você quer ficar ainda mais bonito, você pode suportar o header de Conteúdo-intervalo que permite que os clientes solicitem um determinado intervalo de bytes do seu arquivo. Isso é particularmente útil para servir arquivos PDF para o Adobe Acrobat, que apenas solicita os pedaços do arquivo que ele precisa para renderizar a página atual. Está um pouco envolvido, mas veja isso para um exemplo .

A melhor maneira de enviar grandes arquivos com php é o header X-Sendfile . Ele permite que o servidor web sirva arquivos muito mais rápido através de mecanismos de cópia zero, como o arquivo de sendfile(2) . É suportado por lighttpd e apache com um plugin .

Exemplo:

 $file = "/absolute/path/to/file"; // can be protected by .htaccess header('X-Sendfile: '.$file); header('Content-type: application/octet-stream'); header('Content-Disposition: attachment; filename="'.basename($file).'"'); // other headers ... exit; 

O servidor lê o header X-Sendfile e envia o arquivo.

Embora fpassthru() tenha sido a minha primeira escolha no passado, o manual do PHP realmente recomenda * usando readfile() , em vez disso, se você estiver apenas despejando o arquivo como é para o cliente.

* “Se você quiser apenas despejar o conteúdo de um arquivo para o buffer de saída, sem primeiro modificá-lo ou buscando um deslocamento particular, você pode querer usar o readfile (), o que lhe poupa a chamada fopen ()”. – Manual do PHP

Se os seus arquivos não forem acessados ​​pelo servidor da web porque o caminho não está no diretório de serviço da Web (htdocs), você pode criar um link simbólico (link simbólico) para essa pasta em seu diretório de serviço da Web para evitar passar todo o tráfego por meio de php.

Você pode fazer algo assim

 ln -s /home/files/big_files_folder /home/www/htdocs 

O uso do php para servir arquivos estáticos é muito mais lento, se você tiver alto tráfego, o consumo de memory será muito grande e pode não lidar com um grande número de solicitações.

Dê uma olhada em fpassthru () . Em versões mais recentes do PHP, isso deve servir os arquivos sem mantê-los em memory, como afirma esse comentário .

Estranho, nem fpassthru () nem readfile () fizeram isso para mim, sempre tiveram um erro de memory. Eu recorreu a usar passthru () sem o ‘f’:

 $name = 'mybigfile.zip'; // send the right headers header("Content-Type: application/zip"); header("Content-Length: " . filesize($name)); // dump the file and stop the script passthru('/bin/cat '.$filename); exit; 

este comando Unix do gato dos executivos e envia sua saída para o navegador.

Comente para slim: o motivo pelo qual você simplesmente não coloca um link simbólico para algum lugar é o espaço na web é SEGURANÇA.

Um dos benefícios de fpassthru () é que esta function pode funcionar não apenas com arquivos, mas com qualquer identificador válido. Soquete, por exemplo.

E readfile () deve ser um pouco mais rápido, devido ao uso do mecanismo de cache do SO, se possível (assim como o arquivo_get_contents ()).

Mais uma dica. fpassthru () segure o identificador aberto, até que o cliente obtenha conteúdo (o que pode exigir bastante tempo na conexão lenta) e, portanto, você deve usar algum mecanismo de bloqueio se o paralelo escrever este arquivo possivel.

As respostas do Python são todas boas. Mas há alguma razão pela qual você não pode criar um diretório web acessível que contenha links simbólicos para os arquivos reais? Pode demorar uma configuração adicional do servidor, mas deve funcionar.