Lo primero que debe hacer es enviar el Accept-Ranges: bytes
encabezado en todas las respuestas, para decirle al cliente que admite contenido parcial. Entonces, si la solicitud con una Range: bytes=x-y
se recibe la cabecera (con x
y y
son números) que analizar el rango del cliente está solicitando, abre el archivo como de costumbre, busque x
bytes adelante y enviar el siguiente y
- x
bytes. También configure la respuesta en HTTP/1.0 206 Partial Content
.
Sin haber probado nada, esto podría funcionar, más o 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);
Es posible que me haya perdido algo obvio y definitivamente he ignorado algunas fuentes potenciales de errores, pero debería ser un comienzo.
Aquí hay una descripción del contenido parcial y encontré información sobre el contenido parcial en la página de documentación de fread .
$length
será negativo.$length = (($matches[2]) ? intval($matches[2]) : $filesize) - $offset;
arregla eso. 2.Content-Range
trata el primer byte como byte0
, por lo que el último byte es$filesize - 1
. Por lo tanto, tiene que serlo($offset + $length - 1)
.EDITAR 2017/01: escribí una biblioteca para hacer esto en PHP> = 7.0 https://github.com/DaveRandom/Resume
EDITAR 2016/02: código reescrito completamente en un conjunto de herramientas modulares y un uso de ejemplo, en lugar de una función monolítica. Se han incorporado las correcciones mencionadas en los comentarios a continuación.
Una solución de trabajo probada (basada en gran medida en la respuesta de Theo anterior) que se ocupa de las descargas reanudables, en un conjunto de algunas herramientas independientes. Este código requiere PHP 5.4 o posterior.
Esta solución solo puede hacer frente a un rango por solicitud, pero bajo cualquier circunstancia con un navegador estándar en el que pueda pensar, esto no debería causar un problema.
Uso de ejemplo:
fuente
$start = $end - intval($range[0]);
debería serrange[1]
Esto funciona 100% super compruébalo, lo estoy usando y ya no hay problemas.
fuente
Si. Soporte de rangos de bytes. Consulte la sección 14.35 de RFC 2616 .
Básicamente significa que debe leer el
Range
encabezado y comenzar a servir el archivo desde el desplazamiento especificado.Esto significa que no puede usar readfile (), ya que sirve para todo el archivo. En su lugar, use fopen () primero, luego fseek () a la posición correcta, y luego use fpassthru () para servir el archivo.
fuente
echo file_get_contents(...)
no funcionó (OOM). Así que no creo que eso sea un problema. PHP 5.3.fpassthru
fallará incluso en archivos de 50 MB. Definitivamente no debería usarlo si está sirviendo archivos grandes en una configuración de servidor débil. Como @Wimmer señala correctamente,fread
+print
es todo lo que necesita en este caso.Una manera realmente agradable de resolver esto sin tener que "rodar su propio" código PHP es usar el módulo mod_xsendfile Apache. Luego, en PHP, simplemente establece los encabezados apropiados. Apache consigue hacer lo suyo.
fuente
XSendFilePath <absolute path> [AllowFileDelete]
( tn123.org/mod_xsendfile/beta ).Si está dispuesto a instalar un nuevo módulo PECL, la forma más sencilla de admitir descargas reanudables con PHP es a través de
http_send_file()
esta.fuente: http://www.php.net/manual/en/function.http-send-file.php
Lo usamos para servir contenido almacenado en bases de datos y funciona como un encanto.
fuente
La respuesta principal tiene varios errores.
bytes a-b
debería significar en[a, b]
lugar de[a, b)
, ybytes a-
no se maneja.Aquí está mi código modificado:
fuente
ini_set('memory_limit', '-1');
?if(!isset($matches[2])) { $end=$fs-1; } else { $end = intval($matches[2]); }
Sí, puede usar el encabezado Range para eso. Debe proporcionar 3 encabezados más al cliente para una descarga completa:
En el caso de una descarga interrumpida, debe verificar el encabezado de solicitud de Rango de la siguiente manera:
Y en este caso, no olvide entregar el contenido con el código de estado 206:
Obtendrá las variables $ start y $ to del encabezado de la solicitud, y usará fseek () para buscar la posición correcta en el archivo.
fuente
Esto funcionó muy bien para mí: https://github.com/pomle/php-serveFilePartial
fuente
Pequeña clase habilitada para compositor que funciona de la misma manera que pecl http_send_file. Esto significa soporte para descargas reanudables y aceleración. https://github.com/diversen/http-send-file
fuente
La reanudación de las descargas en HTTP se realiza a través del
Range
encabezado. Si la solicitud contiene unRange
encabezado y otros indicadores (pIf-Match
. Ej. ,If-Unmodified-Since
) Indican que el contenido no ha cambiado desde que se inició la descarga, proporcione un código de respuesta 206 (en lugar de 200), indique el rango de bytes que está devolviendo. en elContent-Range
encabezado, luego proporcione ese rango en el cuerpo de la respuesta.Sin embargo, no sé cómo hacer eso en PHP.
fuente
¡Gracias Theo! su método no funcionó directamente para la transmisión de divx porque descubrí que el reproductor de divx estaba enviando rangos como bytes = 9932800-
pero me mostró cómo hacerlo así que gracias: D
fuente
Puede usar el siguiente código para el soporte de solicitudes de rango de bytes en cualquier navegador
fuente