已掉线,重新登录

首页 > 绿虎论坛 > 历史版块 > 编程 > PHP

标题: [源代码] PHP 解析 Torrent 文件

作者: @Ta

时间: 2016-03-14发布,2016-03-16修改

点击: 3840

先上源代码:说明见下文:
<?php
class TorrentInfo{
    public $Info;
    private $fileBytes;
    private $fileLength;
    public function GetBytes($startPos, $length){
        return substr($this->fileBytes, $startPos, $length);
    }
    private function readBytes($startPos) {
        for($i = $startPos; $i < $this->fileLength; $i++){
            if($this->fileBytes[$i] == ':'){
                $valueLength = intval(substr($this->fileBytes, $startPos, $i - $startPos));
                $value = substr($this->fileBytes, $i + 1, $valueLength);
                return array($value, $startPos, $i + $valueLength + 1);
            }
        }
    }
    private function readInteger($startPos){
        for($i = $startPos; $i < $this->fileLength; $i++){
            if($this->fileBytes[$i] == 'e'){
                $r0 = intval(substr($this->fileBytes, $startPos, $i - $startPos));
                return array($r0, $startPos - 1, $i + 1);
            }
        }
    }
    private function readDict($startPos){
        $ret = array(array(), $startPos - 1, 0);
        $isTitle = TRUE;
        $lastTitle = "ROOT";
        while($startPos < $this->fileLength){
            if($isTitle){
                if ($this->fileBytes[$startPos] == 'e'){
                    $ret[2] = $startPos + 1;
                    return $ret;
                }
                $isTitle = FALSE;
                $title = $this->readBytes($startPos);
                $startPos = $title[2];
                $lastTitle = $title[0];
            }else{
                $isTitle = TRUE;
                switch($this->fileBytes[$startPos]){
                    case 'd':
                        $r0 = $this->readDict($startPos + 1);
                        $ret[0][$lastTitle] = $r0;
                        $startPos = $r0[2];
                        break;
                    case 'l':
                        $r0 = $this->readList($startPos + 1);
                        $ret[0][$lastTitle] = $r0;
                        $startPos = $r0[2];
                        break;
                    case 'i':
                        $r0 = $this->readInteger($startPos + 1);
                        $ret[0][$lastTitle] = $r0;
                        $startPos = $r0[2];
                        break;
                    default:
                        $r0 = $this->readBytes($startPos);
                        $startPos = $r0[2];
                        $ret[0][$lastTitle] = $r0;
                        break;
                }
            }
        }
    }
    private function readList($startPos){
        $ret = array(array(), $startPos - 1, 0);
        while($startPos < $this->fileLength){
            switch($this->fileBytes[$startPos]){
                case 'e':
                    $ret[2] = $startPos + 1;
                    return $ret;
                case 'd':
                    $r0 = $this->readDict($startPos + 1);
                    $ret[0][] = $r0;
                    $startPos = $r0[2];
                    break;
                case 'l':
                    $r0 = $this->readList($startPos + 1);
                    $ret[0][] = $r0;
                    $startPos = $r0[2];
                    break;
                case 'i':
                    $r0 = $this->readInteger($startPos + 1);
                    $ret[0][] = $r0;
                    $startPos = $r0[2];
                    break;
                default:
                    $r0 = $this->readBytes($startPos);
                    $startPos = $r0[2];
                    $ret[0][] = $r0;
                    break;
            }
        }
    }
    public function __construct($fileContents){
        $this->fileBytes = $fileContents;
        $this->fileLength = strlen($fileContents);
        $this->Info = $this->readDict(1);
    }
}
查阅了各种资料,自己也着手分析了.torrent文件的结构,总算是弄明白了,
这个文件总是返回一个长度为3的数组,其中第一项为值,第二项为该值开头所处文件中的位置,第三项为该值结尾所处文件中的位置。
其中也存在嵌套关系,可以自己拿去分析。
用法(显示所有信息)(假设上面的文件被保存为“xtorrent.php”,要分析的.torrent文件名为“test.torrent”):
<?php
include_once './xtorrent.php';
$xt = new TorrentInfo(file_get_contents('./test.torrent'));
header('Content-Type: text/plain; charset=ascii');
print_r($xt->Info);
可以用于转换磁力链接:
<?php
include_once './xtorrent.php';
$xt = new TorrentInfo(file_get_contents('./test.torrent'));
header('Content-Type: text/plain; charset=ascii');
echo 'magnet:?xt=urn:btih:';
echo sha1($xt->GetBytes($xt->Info[0]['info'][1], $xt->Info[0]['info'][2] - $xt->Info[0]['info'][1]));
/* 这种磁力链接有两种形式,一种就像上面这种直接是20个16进制字符组成的sha-1哈希计算值,
还有一种形式是经过Base32编码的版本:
 echo base32_encode(sha1($xt->GetBytes($xt->Info[0]['info'][1], $xt->Info[0]['info'][2] - $xt->Info[0]['info'][1]), TRUE));
当然,第二种版本是不能直接用的,因为PHP并不内置base32编码功能。 */
Mar 16, 2016更新 添加Nodejs版本,用法与PHP版本类似,但是Info被改成了小写的info。
function torrentInfo(fileBuffer) {
  torrentInfo.fileBuffer = fileBuffer;
  torrentInfo.info = torrentInfo.readDict(0);
  return torrentInfo;
}

torrentInfo.readBytes = function(startPos) {
  for(var i = startPos; i < torrentInfo.fileBuffer.length; i++){
    if (torrentInfo.fileBuffer[i] == 0x3a) {
      var contentEndPos = parseInt(torrentInfo.fileBuffer.toString('ascii', startPos, i)) + i + 1;
      return [torrentInfo.fileBuffer.slice(i + 1, contentEndPos), startPos, contentEndPos];
    }
  }
}

torrentInfo.readInteger = function(startPos) {
  startPos++;
  for (var i = startPos; i < torrentInfo.fileBuffer.length; i++) {
    if (torrentInfo.fileBuffer[i] == 0x65) {
      var r0 = parseInt(torrentInfo.fileBuffer.toString('ascii', startPos, i));
      return [r0, startPos - 1, i + 1];
    }
  }
}

torrentInfo.readDict = function(startPos){
  var ret = [{}, startPos, 0];
  startPos++;
  var isTitle = true;
  var lastTitle = ':ROOT:';
  while (startPos < torrentInfo.fileBuffer.length) {
    var r0;
    if (isTitle) {
      if (torrentInfo.fileBuffer[startPos] == 0x65) {
        ret[2] = startPos + 1;
        return ret;
      }
      isTitle = false;
      title = torrentInfo.readBytes(startPos);
      lastTitle = title[0].toString();
      startPos = title[2];
      continue;
    }
    switch (torrentInfo.fileBuffer[startPos]) {
      case 0x64:
        r0 = torrentInfo.readDict(startPos);
        ret[0][lastTitle] = r0;
        startPos = r0[2];
        break;
      case 0x6c:
        r0 = torrentInfo.readList(startPos);
        ret[0][lastTitle] = r0;
        startPos = r0[2];
        break;
      case 0x69:
        r0 = torrentInfo.readInteger(startPos);
        ret[0][lastTitle] = r0;
        startPos = r0[2];
        break;
      default:
        r0 = torrentInfo.readBytes(startPos);
        ret[0][lastTitle] = r0;
        startPos = r0[2];
        break;
    }
    isTitle = true;
  }
}
torrentInfo.readList = function(startPos){
  var ret = [[], startPos, 0];
  startPos++;
  while (startPos < torrentInfo.fileBuffer.length) {
    var r0;
    switch (torrentInfo.fileBuffer[startPos]) {
      case 0x65:
        ret[2] = startPos + 1;
        return ret;
      case 0x64:
        r0 = torrentInfo.readDict(startPos);
        ret[0].push(r0);
        startPos = r0[2];
        break;
      case 0x6c:
        r0 = torrentInfo.readList(startPos);
        ret[0].push(r0);
        startPos = r0[2];
        break;
      case 0x69:
        r0 = torrentInfo.readInteger(startPos);
        ret[0].push(r0);
        startPos = r0[2];
        break;
      default:
        r0 = torrentInfo.readBytes(startPos);
        ret[0].push(r0);
        startPos = r0[2];
        break;
    }
  }
}

[隐藏样式|查看源码]


『回复列表(6|显示机器人聊天)』

2. 好久没看到新鲜的代码了(っ´Ι`)っ
已保存
(/@Ta/2016-03-15 07:12//)

3. 已保存
(/@Ta/2016-03-15 09:30//)

4. @梦浪的小虾米,怎么用……
(/@Ta/2016-03-15 13:25//)

5. @穴儿
这是一个PHP中的对象

我们主要用的是该对象中的Info变量,以下记作$self->Info。

这个变量是一个数组,包含3个元素,第一个元素是本身的值,可能是一个整数,字符串,一个数组或一个关系数组。第二个元素是该字段第一个字节处于文件中的位置,是一个整数。第三个元素是该字段最后一个字节所处于文件中的位置,是一个整数。

Torrent文件的结构类似于JSON,$self->Info就是其中第一个字段。

具体怎么用,试一试print_r($self->Info)就知道了。
(/@Ta/2016-03-15 14:44//)

6. 看不懂,
(/@Ta/2016-03-16 18:23//)

7. @穴儿,<?php
include_once './xtorrent.php';
$xt = new TorrentInfo(file_get_contents('./test.torrent'));
header('Content-Type: text/plain; charset=ascii');
print_r($xt->Info);人家这里写这么明白
(/@Ta/2016-03-16 19:50//)

回复需要登录

8月14日 04:15 星期四

本站由hu60wap6驱动

备案号: 京ICP备18041936号-1