A primeira coisa que você precisa fazer é enviar o Accept-Ranges: bytes
cabeçalho em todas as respostas, para informar ao cliente que você oferece suporte a conteúdo parcial. Então, se pedido com um Range: bytes=x-y
cabeçalho é recebido (com x
e y
sendo números) você analisar o intervalo o cliente está solicitando, abra o arquivo como de costume, procure x
bytes em frente e enviar os próximos y
- x
bytes. Defina também a resposta como HTTP/1.0 206 Partial Content
.
Sem ter testado nada, isso poderia funcionar, mais ou menos:
$filesize = filesize($file);
$offset = 0;
$length = $filesize;
if ( isset($_SERVER['HTTP_RANGE']) ) {
// if the HTTP_RANGE header is set we're dealing with partial content
$partialContent = true;
// find the requested range
// this might be too simplistic, apparently the client can request
// multiple ranges, which can become pretty complex, so ignore it for now
preg_match('/bytes=(\d+)-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches);
$offset = intval($matches[1]);
$length = intval($matches[2]) - $offset;
} else {
$partialContent = false;
}
$file = fopen($file, 'r');
// seek to the requested offset, this is 0 if it's not a partial content request
fseek($file, $offset);
$data = fread($file, $length);
fclose($file);
if ( $partialContent ) {
// output the right headers for partial content
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $offset . '-' . ($offset + $length) . '/' . $filesize);
}
// output the regular HTTP headers
header('Content-Type: ' . $ctype);
header('Content-Length: ' . $filesize);
header('Content-Disposition: attachment; filename="' . $fileName . '"');
header('Accept-Ranges: bytes');
// don't forget to send the data too
print($data);
Posso ter perdido algo óbvio e definitivamente ignorei algumas fontes potenciais de erros, mas deve ser um começo.
Há uma descrição do conteúdo parcial aqui e eu encontrei algumas informações sobre o conteúdo parcial na página de documentação do fread .
$length
será negativo.$length = (($matches[2]) ? intval($matches[2]) : $filesize) - $offset;
corrige isso. 2.Content-Range
trata o primeiro byte como byte0
, então o último byte é$filesize - 1
. Portanto, tem que ser($offset + $length - 1)
.EDIT 2017/01 - escrevi uma biblioteca para fazer isso em PHP> = 7.0 https://github.com/DaveRandom/Resume
EDIT 2016/02 - Código completamente reescrito para um conjunto de ferramentas modulares um exemplo de uso, ao invés de uma função monolítica. As correções mencionadas nos comentários abaixo foram incorporadas.
Uma solução testada e funcional (baseada fortemente na resposta de Theo acima) que lida com downloads recuperáveis, em um conjunto de algumas ferramentas autônomas. Este código requer PHP 5.4 ou posterior.
Essa solução ainda pode lidar com apenas um intervalo por solicitação, mas em qualquer circunstância com um navegador padrão que eu possa imaginar, isso não deve causar problemas.
Exemplo de uso:
fonte
$start = $end - intval($range[0]);
deveria serrange[1]
Isso funciona 100% super, verifique se estou usando e sem problemas mais.
fonte
Sim. Byteranges de suporte. Consulte a seção 14.35 do RFC 2616 .
Basicamente, significa que você deve ler o
Range
cabeçalho e começar a servir o arquivo a partir do deslocamento especificado.Isso significa que você não pode usar readfile (), uma vez que serve o arquivo inteiro. Em vez disso, use fopen () primeiro, depois fseek () para a posição correta e, em seguida, use fpassthru () para servir o arquivo.
fonte
echo file_get_contents(...)
não funcionou (OOM). Portanto, não acho que isso seja um problema. PHP 5.3.fpassthru
falhará até em arquivos de 50 MB. Definitivamente, você não deve usá-lo se estiver servindo arquivos grandes em uma configuração de servidor fraca. Como @Wimmer corretamente aponta,fread
+print
é tudo o que você precisa neste caso.Uma maneira muito boa de resolver isso sem ter que "lançar seu próprio" código PHP é usar o módulo mod_xsendfile do Apache. Então, no PHP, você apenas define os cabeçalhos apropriados. O Apache faz seu trabalho.
fonte
XSendFilePath <absolute path> [AllowFileDelete]
( tn123.org/mod_xsendfile/beta ).Se você estiver disposto a instalar um novo módulo PECL, a maneira mais fácil de suportar downloads retomáveis com PHP é através
http_send_file()
, como estefonte: http://www.php.net/manual/en/function.http-send-file.php
Nós o usamos para servir conteúdo armazenado em banco de dados e funciona perfeitamente!
fonte
A resposta principal contém vários bugs.
bytes a-b
deve significar em[a, b]
vez de[a, b)
ebytes a-
não é manipulado.Aqui está meu código modificado:
fonte
ini_set('memory_limit', '-1');
?if(!isset($matches[2])) { $end=$fs-1; } else { $end = intval($matches[2]); }
Sim, você pode usar o cabeçalho Range para isso. Você precisa fornecer mais 3 cabeçalhos ao cliente para um download completo:
Em seguida, para um download interrompido, você precisa verificar o cabeçalho da solicitação Range:
E, neste caso, não se esqueça de servir o conteúdo com código de status 206:
Você obterá as variáveis $ start e $ to do cabeçalho da solicitação e usará fseek () para buscar a posição correta no arquivo.
fonte
Isso funcionou muito bem para mim: https://github.com/pomle/php-serveFilePartial
fonte
Classe habilitada para compositor pequeno que funciona da mesma maneira que pecl http_send_file. Isso significa suporte para downloads recuperáveis e aceleração. https://github.com/diversen/http-send-file
fonte
A retomada dos downloads em HTTP é feita por meio do
Range
cabeçalho. Se a solicitação contém umRange
cabeçalho e se outros indicadores (por exemploIf-Match
,If-Unmodified-Since
) indicam que o conteúdo não mudou desde o início do download, você fornece um código de resposta 206 (em vez de 200), indica o intervalo de bytes que está retornando noContent-Range
cabeçalho e, em seguida, forneça esse intervalo no corpo da resposta.Não sei como fazer isso em PHP, no entanto.
fonte
Obrigado Theo! seu método não funcionou diretamente para streaming divx porque descobri que o player divx estava enviando intervalos como bytes = 9932800-
mas me mostrou como fazer isso, obrigado: D
fonte
Você pode usar o código abaixo para suporte de solicitação de intervalo de bytes em qualquer navegador
fonte