La première chose à faire est d'envoyer l'en- Accept-Ranges: bytes
tête dans toutes les réponses, pour indiquer au client que vous prenez en charge le contenu partiel. Ensuite, si la demande avec un en- Range: bytes=x-y
tête est reçu (avec x
et y
étant des nombres) vous analysez la plage le client demande, ouvrez le fichier comme d' habitude, Seek x
octets avant et d' envoyer les prochains y
- x
octets. Définissez également la réponse sur HTTP/1.0 206 Partial Content
.
Sans avoir testé quoi que ce soit, cela pourrait fonctionner, plus ou moins:
$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);
J'ai peut-être manqué quelque chose d'évident, et j'ai très certainement ignoré certaines sources potentielles d'erreurs, mais cela devrait être un début.
Il y a une description du contenu partiel ici et j'ai trouvé des informations sur le contenu partiel sur la page de documentation de fread .
$length
sera négatif.$length = (($matches[2]) ? intval($matches[2]) : $filesize) - $offset;
corrige cela. 2.Content-Range
traite le premier octet comme un octet0
, donc le dernier octet l'est$filesize - 1
. Par conséquent, il doit l'être($offset + $length - 1)
.EDIT 2017/01 - J'ai écrit une bibliothèque pour faire cela en PHP> = 7.0 https://github.com/DaveRandom/Resume
EDIT 2016/02 - Code complètement réécrit en un ensemble d'outils modulaires, un exemple d'utilisation, plutôt qu'une fonction monolithique. Les corrections mentionnées dans les commentaires ci-dessous ont été intégrées.
Une solution testée et fonctionnelle (largement basée sur la réponse de Theo ci-dessus) qui traite des téléchargements avec reprise, dans un ensemble de quelques outils autonomes. Ce code nécessite PHP 5.4 ou version ultérieure.
Cette solution ne peut toujours faire face qu'à une plage par requête, mais en toute circonstance avec un navigateur standard auquel je pense, cela ne devrait pas poser de problème.
Exemple d'utilisation:
la source
$start = $end - intval($range[0]);
devrait êtrerange[1]
Cela fonctionne à 100% super vérifiez que je l'utilise et plus de problèmes.
la source
Oui. Prend en charge les byteranges. Voir la section 14.35 de la RFC 2616 .
Cela signifie essentiellement que vous devez lire l'en-
Range
tête et commencer à servir le fichier à partir du décalage spécifié.Cela signifie que vous ne pouvez pas utiliser readfile (), car cela sert l'ensemble du fichier. À la place, utilisez d'abord fopen () , puis fseek () à la bonne position, puis utilisez fpassthru () pour servir le fichier.
la source
echo file_get_contents(...)
n'a pas fonctionné (MOO). Je ne pense donc pas que ce soit un problème. PHP 5.3.fpassthru
échouera même sur des fichiers de 50 Mo. Vous ne devriez certainement pas l'utiliser si vous servez des fichiers volumineux sur une configuration de serveur faible. Comme @Wimmer le souligne correctement,fread
+print
est tout ce dont vous avez besoin dans ce cas.Un très bon moyen de résoudre ce problème sans avoir à "lancer votre propre" code PHP est d'utiliser le module Apache mod_xsendfile. Ensuite, en PHP, il vous suffit de définir les en-têtes appropriés. Apache doit faire son travail.
la source
XSendFilePath <absolute path> [AllowFileDelete]
( tn123.org/mod_xsendfile/beta ).Si vous êtes prêt à installer un nouveau module PECL, le moyen le plus simple de prendre en charge les téléchargements avec reprise avec PHP consiste à utiliser
http_send_file()
, comme cecisource: http://www.php.net/manual/en/function.http-send-file.php
Nous l'utilisons pour servir du contenu stocké dans une base de données et cela fonctionne comme un charme!
la source
La première réponse a plusieurs bugs.
bytes a-b
devrait signifier[a, b]
au lieu de[a, b)
, etbytes a-
n'est pas gérée.Voici mon code modifié:
la source
ini_set('memory_limit', '-1');
?if(!isset($matches[2])) { $end=$fs-1; } else { $end = intval($matches[2]); }
Oui, vous pouvez utiliser l'en-tête Range pour cela. Vous devez donner 3 autres en-têtes au client pour un téléchargement complet:
Que pour un téléchargement interrompu, vous devez vérifier l'en-tête de la requête Range en:
Et dans ce cas, n'oubliez pas de servir le contenu avec le code d'état 206:
Vous obtiendrez les variables $ start et $ to à partir de l'en-tête de la requête, et utiliserez fseek () pour rechercher la position correcte dans le fichier.
la source
Cela a très bien fonctionné pour moi: https://github.com/pomle/php-serveFilePartial
la source
Petite classe activée pour composer qui fonctionne de la même manière que pecl http_send_file. Cela signifie la prise en charge des téléchargements avec reprise et de l'accélération. https://github.com/diversen/http-send-file
la source
La reprise des téléchargements en HTTP se fait via l'en-
Range
tête. Si la requête contient un en-Range
tête, et si d'autres indicateurs (par exempleIf-Match
,If-Unmodified-Since
) indiquent que le contenu n'a pas changé depuis le début du téléchargement, vous donnez un code de réponse 206 (au lieu de 200), indiquez la plage d'octets que vous renvoyez dans l'en-Content-Range
tête, puis indiquez cette plage dans le corps de la réponse.Je ne sais pas comment faire ça en PHP, cependant.
la source
Merci Théo! votre méthode ne fonctionnait pas directement pour le streaming divx car j'ai trouvé que le lecteur divx envoyait des plages telles que bytes = 9932800-
mais il m'a montré comment le faire alors merci: D
la source
Vous pouvez utiliser le code ci-dessous pour la prise en charge des demandes de plage d'octets dans n'importe quel navigateur
la source