2009-09-08 6 views
13

J'écris un script PHP qui permet à l'utilisateur de télécharger un fichier. Fondamentalement, l'idée est d'empêcher le téléchargement du fichier plus de X fois, car il s'agit de contenu payant, et le lien ne doit pas être réparti.Existe-t-il une bonne implémentation du téléchargement partiel de fichiers en PHP?

Étant donné que les fichiers seront assez volumineux, il devrait être bon d'implémenter la reprise. J'ai lu the standard, mais c'est assez long et permet une certaine flexibilité. Puisque je dois le faire rapidement, je préférerais une implémentation stable et testée de cette fonctionnalité.

Quelqu'un peut-il me diriger vers un tel script?

Répondre

8

Il semble que j'ai trouvé ce dont j'avais besoin moi-même. Alors que d'autres peuvent en bénéficier, voici le lien: http://www.coneural.org/florian/papers/04_byteserving.php

Et juste au cas où la page d'origine arrête pour travailler (le script est assez vieux déjà), voici une copie:

<?php 
/* 

The following byte serving code is (C) 2004 Razvan Florian. You may find the latest version at 
http://www.coneural.org/florian/papers/04_byteserving.php 

*/ 
function set_range($range, $filesize, &$first, &$last){ 
    /* 
    Sets the first and last bytes of a range, given a range expressed as a string 
    and the size of the file. 

    If the end of the range is not specified, or the end of the range is greater 
    than the length of the file, $last is set as the end of the file. 

    If the begining of the range is not specified, the meaning of the value after 
    the dash is "get the last n bytes of the file". 

    If $first is greater than $last, the range is not satisfiable, and we should 
    return a response with a status of 416 (Requested range not satisfiable). 

    Examples: 
    $range='0-499', $filesize=1000 => $first=0, $last=499 . 
    $range='500-', $filesize=1000 => $first=500, $last=999 . 
    $range='500-1200', $filesize=1000 => $first=500, $last=999 . 
    $range='-200', $filesize=1000 => $first=800, $last=999 . 

    */ 
    $dash=strpos($range,'-'); 
    $first=trim(substr($range,0,$dash)); 
    $last=trim(substr($range,$dash+1)); 
    if ($first=='') { 
    //suffix byte range: gets last n bytes 
    $suffix=$last; 
    $last=$filesize-1; 
    $first=$filesize-$suffix; 
    if($first<0) $first=0; 
    } else { 
    if ($last=='' || $last>$filesize-1) $last=$filesize-1; 
    } 
    if($first>$last){ 
    //unsatisfiable range 
    header("Status: 416 Requested range not satisfiable"); 
    header("Content-Range: */$filesize"); 
    exit; 
    } 
} 

function buffered_read($file, $bytes, $buffer_size=1024){ 
    /* 
    Outputs up to $bytes from the file $file to standard output, $buffer_size bytes at a time. 
    */ 
    $bytes_left=$bytes; 
    while($bytes_left>0 && !feof($file)){ 
    if($bytes_left>$buffer_size) 
     $bytes_to_read=$buffer_size; 
    else 
     $bytes_to_read=$bytes_left; 
    $bytes_left-=$bytes_to_read; 
    $contents=fread($file, $bytes_to_read); 
    echo $contents; 
    flush(); 
    } 
} 

function byteserve($filename){ 
    /* 
    Byteserves the file $filename. 

    When there is a request for a single range, the content is transmitted 
    with a Content-Range header, and a Content-Length header showing the number 
    of bytes actually transferred. 

    When there is a request for multiple ranges, these are transmitted as a 
    multipart message. The multipart media type used for this purpose is 
    "multipart/byteranges". 
    */ 

    $filesize=filesize($filename); 
    $file=fopen($filename,"rb"); 

    $ranges=NULL; 
    if ($_SERVER['REQUEST_METHOD']=='GET' && isset($_SERVER['HTTP_RANGE']) && $range=stristr(trim($_SERVER['HTTP_RANGE']),'bytes=')){ 
    $range=substr($range,6); 
    $boundary='g45d64df96bmdf4sdgh45hf5';//set a random boundary 
    $ranges=explode(',',$range); 
    } 

    if($ranges && count($ranges)){ 
    header("HTTP/1.1 206 Partial content"); 
    header("Accept-Ranges: bytes"); 
    if(count($ranges)>1){ 
     /* 
     More than one range is requested. 
     */ 

     //compute content length 
     $content_length=0; 
     foreach ($ranges as $range){ 
     set_range($range, $filesize, $first, $last); 
     $content_length+=strlen("\r\n--$boundary\r\n"); 
     $content_length+=strlen("Content-type: application/pdf\r\n"); 
     $content_length+=strlen("Content-range: bytes $first-$last/$filesize\r\n\r\n"); 
     $content_length+=$last-$first+1;   
     } 
     $content_length+=strlen("\r\n--$boundary--\r\n"); 

     //output headers 
     header("Content-Length: $content_length"); 
     //see http://httpd.apache.org/docs/misc/known_client_problems.html for an discussion of x-byteranges vs. byteranges 
     header("Content-Type: multipart/x-byteranges; boundary=$boundary"); 

     //output the content 
     foreach ($ranges as $range){ 
     set_range($range, $filesize, $first, $last); 
     echo "\r\n--$boundary\r\n"; 
     echo "Content-type: application/pdf\r\n"; 
     echo "Content-range: bytes $first-$last/$filesize\r\n\r\n"; 
     fseek($file,$first); 
     buffered_read ($file, $last-$first+1);   
     } 
     echo "\r\n--$boundary--\r\n"; 
    } else { 
     /* 
     A single range is requested. 
     */ 
     $range=$ranges[0]; 
     set_range($range, $filesize, $first, $last); 
     header("Content-Length: ".($last-$first+1)); 
     header("Content-Range: bytes $first-$last/$filesize"); 
     header("Content-Type: application/pdf"); 
     fseek($file,$first); 
     buffered_read($file, $last-$first+1); 
    } 
    } else{ 
    //no byteserving 
    header("Accept-Ranges: bytes"); 
    header("Content-Length: $filesize"); 
    header("Content-Type: application/pdf"); 
    readfile($filename); 
    } 
    fclose($file); 
} 

function serve($filename, $download=0){ 
    //Just serves the file without byteserving 
    //if $download=true, then the save file dialog appears 
    $filesize=filesize($filename); 
    header("Content-Length: $filesize"); 
    header("Content-Type: application/pdf"); 
    $filename_parts=pathinfo($filename); 
    if($download) header('Content-disposition: attachment; filename='.$filename_parts['basename']); 
    readfile($filename); 
} 

//unset magic quotes; otherwise, file contents will be modified 
set_magic_quotes_runtime(0); 

//do not send cache limiter header 
ini_set('session.cache_limiter','none'); 


$filename='myfile.pdf'; //this is the PDF file that will be byteserved 
byteserve($filename); //byteserve it! 
?> 
+0

Le code de réponse Vilx fonctionne très bien pour moi. Je en ai besoin pour l'ouverture de PDF dans la fenêtre du navigateur. et maintenant ça marche. –

+0

Il fonctionne bien pour les fichiers locaux, mais lorsqu'il est utilisé pour télécharger des fichiers distants, il ne peut pas générer de liens résumables. –

+0

@SiyamakShahpasand - Que voulez-vous dire - "télécharger des fichiers distants"? Si le fichier à télécharger n'est pas sur votre serveur, vous ne le téléchargez pas, vous le redirigez. –

0

Voir http://us3.php.net/manual/en/function.fread.php

Une alternative consiste à laisser le serveur Web gérer http en redirigeant vers le fichier en question. Un script PHP peut effectuer toutes les vérifications nécessaires (sécurité, authentification, validation du fichier, incrémentation du nombre de téléchargements) et toutes les autres tâches avant d'appeler l'en-tête ("Location $ urltofile");

J'ai testé cela avec Apache. Le téléchargement d'interruption/reprise fonctionne. La configuration du type mime du serveur déterminera le comportement du client. Pour apache, si les valeurs par défaut dans mime.types ne conviennent pas, les directives de configuration pour mod_mime pourraient aller dans un fichier .htaccess dans le répertoire du fichier à télécharger. Si vraiment nécessaire, ceux-ci pourraient même être écrits par le script PHP avant qu'il ne redirige.

+1

L'idée de l'en-tête ('Location:') est mauvaise, car la validation est triviale à contourner. mais j'ai réussi à trouver le code que vous avez mentionné sur le site: Dommage qu'il soit protégé par les droits d'auteur et que je doive contacter le site pour pouvoir l'utiliser, mais cela semble toujours valable. –

1

Sur cette base:

http://w-shadow.com/blog/2007/08/12/how-to-force-file-download-with-php/

(que vous pouvez utiliser aussi)

J'ai fait une petite lib qui fait ce que fait l'extension http_send_file de PECL:

http://php.net/manual/en/function.http-send-file.php

(que vous pouvez utiliser aussi)

La lib ressemble à la http_send_file, mais si vous n'avez pas la possibilité d'installer le PECL lib, vous pouvez utiliser le http-send-lib:

https://github.com/diversen/http-send-file

Questions connexes