在PHP中部分文件下载是否有很好的实现? [英] Is there a good implementation of partial file downloading in PHP?

查看:102
本文介绍了在PHP中部分文件下载是否有很好的实现?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编写一个允许用户下载文件的PHP脚本。基本上,这个想法是防止文件下载超过X次,因为它是付费内容,链接不应该传播。



由于文件将相当大,实施恢复应该很好。我已阅读标准,但它是相当长,允许一些灵活性。因为我需要快速完成,所以我更喜欢这个功能的稳定且经过测试的实现。



任何人都可以指出我这样一个脚本?

解决方案

似乎我找到了我需要的东西。所以其他人可能从中受益,这里是链接: http://www.coneural.org/florian/papers/04_byteserving.php



为了防止原始页面停止工作(脚本已经很旧了),这里是一个副本:

 <?php 
/ *

以下字节服务代码是(C)2004 Razvan Florian。您可以在
找到最新版本http://www.coneural.org/florian/papers/04_byteserving.php

* /
函数set_range($ range,$ filesize ,& $ first,& $ last){
/ *
设置范围的第一个和最后一个字节,给定以字符串
表示的范围和文件的大小。

如果未指定范围的结尾,或者范围的结束比文件的长度更大
,则将$ last设置为文件的结尾。

如果没有指定范围的开头,则
之后的值的意思是获取文件的最后n个字节。

如果$ first大于$ last,范围是不可满足的,我们应该
返回状态为416(请求的范围不可满足)的响应。

示例:
$ 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 ==''){
//后缀字节范围:获取最后n个字节
$ 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){
//不满足范围
header(状态:416请求的范围不可满足);
header(Content-Range:* / $ filesize);
退出;
}
}

函数buffered_read($ file,$ bytes,$ buffer_size = 1024){
/ *
从文件$文件到标准输出,$ buffer_size一次。
* /
$ 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();
}
}

函数byteserve($ filename){
/ *
代码文件$ filename。

当有单个范围的请求时,内容被传输
,内容范围标头和Content-Length标题显示实际传输的字节数


当有多个范围的请求时,它们以
多部分消息的形式传输。用于此目的的多部分媒体类型是
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'; //设置一个随机边界
$ ranges = explode(',',$ range);
}

if($ ranges&& count($ ranges)){
header(HTTP / 1.1 206 Partial content);
header(Accept-Ranges:bytes);
if(count($ ranges)> 1){
/ *
请求多个范围。
* /

//计算内容长度
$ content_length = 0;
foreach($ ranges as $ range){
set_range($ range,$ filesize,$ first,$ last);
$ content_length + = strlen(\r\\\
- $ boundary\r\\\
);
$ content_length + = strlen(Content-type:application / pdf\r\\\
);
$ content_length + = strlen(Content-range:bytes $ first- $ last / $ filesize\r\\\
\r\\\
);
$ content_length + = $ last- $ first + 1;
}
$ content_length + = strlen(\r\\\
- $ boundary - \r\\\
);

//输出标题
header(Content-Length:$ content_length);
//参见http://httpd.apache.org/docs/misc/known_client_problems.html,讨论x-byteranges与byteranges
header(Content-Type:multipart / x-byteranges ; boundary = $ boundary);

//输出内容
foreach($ ranges as $ range){
set_range($ range,$ filesize,$ first,$ last);
echo\r\\\
- $ boundary\r\\\
;
echoContent-type:application / pdf\r
echoContent-range:bytes $ first- $ last / $ filesize\r\\\
\r\\\
;
fseek($ file,$ first);
buffered_read($ file,$ last- $ first + 1);
}
echo\r\\\
- $ boundary - \r\\\
;
} else {
/ *
请求单个范围。
* /
$ 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);
}

函数服务($ filename,$ download = 0){
//只是为文件服务而不需要
//如果$ download = true,那么保存文件对话框出现
$ 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;否则文件内容将被修改
set_magic_quotes_runtime(0);

//不发送缓存限制器头
ini_set('session.cache_limiter','none');


$ filename ='myfile.pdf'; //这是将被字节化的PDF文件
byteserve($ filename); // byteserve it!
?>


I'm writing a PHP script that allows the user to download a file. Basically the idea is to prevent the file being downloaded more than X times, since it is paid content, and the link should not be spread around.

Since the files will be pretty large, it should be good to implement resuming. I've read the standard, but it's pretty long and allows for some flexibility. Since I need to get it done quickly, I'd prefer a stable, tested implementation of this feature.

Can anyone point me to such a a script?

解决方案

Seems that I found what I needed myself. So that other may benefit from this, here is the link: http://www.coneural.org/florian/papers/04_byteserving.php

And just in case the original page stops to work (the script is pretty old already), here is a copy of it:

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

这篇关于在PHP中部分文件下载是否有很好的实现?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆