2009-09-08 32 views
13

Estoy escribiendo un script PHP que permite al usuario descargar un archivo. Básicamente, la idea es evitar que el archivo se descargue más de X veces, ya que es contenido pago, y el enlace no se debe difundir.¿Existe una buena implementación de la descarga parcial de archivos en PHP?

Dado que los archivos serán bastante grandes, debería ser bueno implementar la reanudación. He leído the standard, pero es bastante largo y permite cierta flexibilidad. Como necesito hacerlo rápidamente, preferiría una implementación estable y probada de esta característica.

¿Alguien me puede indicar un script así?

Respuesta

8

Parece que encontré lo que necesitaba. De manera que otro puede beneficiarse de esto, aquí está el enlace: http://www.coneural.org/florian/papers/04_byteserving.php

Y por si acaso la página original se detiene a trabajar (el guión es bastante viejo ya), aquí es una copia de la misma:

<?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

El código de respuesta de Vilx funciona muy bien para mí. Lo necesito para abrir el PDF en la ventana del navegador. y ahora funciona. –

+0

Funciona bien para archivos locales, pero cuando se usa para descargar archivos remotos, no puede generar enlaces reanudables. –

+0

@SiyamakShahpasand - ¿Qué quiere decir con "descargar archivos remotos"? Si el archivo que debe descargarse no está en su servidor, entonces no está descargando, está redirigiendo. –

0

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

Una alternativa es permitir que el servidor web pueda manejar http redirigiendo al archivo en cuestión.

Una secuencia de comandos PHP puede hacer las comprobaciones necesarias (seguridad, autenticación, validar el archivo, incrementar el conteo de descargas) y cualquier otra tarea antes de llamar al encabezado ("Ubicación $ urltofile");

He probado esto con apache. La interrupción/reanudar descarga funciona. La configuración del tipo mime del servidor determinará el comportamiento del cliente. Para Apache, si los valores predeterminados en mime.types no son adecuados, las directivas de configuración para mod_mime podrían ir en un archivo .htaccess en el directorio del archivo para descargar. Si es realmente necesario, estos podrían incluso escribirse mediante el script PHP antes de que redirija.

+1

La idea del encabezado ('Ubicación:') es mala, porque la validación es trivial para eludir. Su enlace también está roto, pero logré encontrar el código que mencionaste en el sitio. Es una pena que tenga derechos de autor y debo contactar al sitio web para que se me permita usarlo. Pero todavía parece válido. –

1

Basado en esto:

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

(el cual también se puede utilizar)

tengo creó una pequeña lib que hace lo que hace la extensión http_send_file de PECL:

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

(que también se puede utilizar)

El lib se asemeja a la http_send_file, pero si usted no tiene la opción de instalar la lib PECL, se puede utilizar el http-send-lib:

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

Cuestiones relacionadas