BT种子结构
- announce:tracker服务器的URL(字符串)
- announce-list(可选):备用tracker服务器列表(列表)
- creation date(可选):种子创建的时间,Unix标准时间格式,从1970 1月1日 00:00:00到创建时间的秒数(整数)
- comment(可选):备注(字符串)
- created by(可选):创建人或创建程序的信息(字符串)
- info:一个字典结构,包含文件的主要信息,为分二种情况:单文件结构或多文件结构(目录)
- 单文件结构如下:
- length:文件长度,单位字节(整数)
- md5sum(可选):长32个字符的文件的MD5校验和,BT不使用这个值,只是为了兼容一些程序所保留!(字符串)
- name:文件名(字符串)
- piece length:每个块的大小,单位字节(整数)
- pieces:每个块的20个字节的SHA1 Hash的值(二进制格式)
- 多文件结构如下:
- files:一个字典结构
- length:文件长度,单位字节(整数)
- md5sum(可选):同单文件结构中相同
- path:文件的路径和名字,是一个列表结构,如”test”test.txt(列表) 列表为l4:test8test.txte
- name:最上层的目录名字(字符串)
- piece length:同单文件结构中相同(整数)
- pieces:同单文件结构中相同 (二进制格式)
代码实现
解析工具类声明
package org.hanmeis.bt;
import org.hanmeis.bt.bean.BitTorrentInfo;
import org.hanmeis.bt.bean.Files;
import org.hanmeis.bt.bean.Info;
import java.io.*;
import java.util.LinkedList;
import java.util.List;
/**
* 代码中并未采用网上现有的递归算法进行解析,而是使用键值匹配的方式。
* 然而当前代码实现中还存在着一些bug,如键和值相同的情况。
* 对此bug修正方法,当前的想法是创建set记录已经出现过的键值情况。
* Created by zhao.wu on 2016/12/8.
*/
public class BitTorrents {
public static BitTorrentInfo parse(File btFile) throws Exception {
return new BitTorrents().analyze(new FileInputStream(btFile));
}
public static BitTorrentInfo parse(String btFilePath) throws Exception {
return new BitTorrents().analyze(new FileInputStream(btFilePath));
}
private BitTorrentInfo analyze(InputStream is) throws Exception {
BitTorrentInfo btInfo = new BitTorrentInfo();
String key = null;
StringBuilder strLengthBuilder = new StringBuilder();
int tempByte;
while ((tempByte = is.read()) != -1) {
char temp = (char) tempByte;
switch (temp) {
case 'i':
StringBuilder itempBuilder = new StringBuilder();
char iTemp;
while ((iTemp = (char) is.read()) != 'e') {
itempBuilder.append(iTemp);
}
btInfo.setValue(key, itempBuilder.toString());
break;
case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
strLengthBuilder.append(temp);
break;
case ':':
int strLen = Integer.parseInt(strLengthBuilder.toString());
strLengthBuilder = new StringBuilder();
byte[] tempBytes = new byte[strLen];
is.read(tempBytes);
if (key != null && key.equals("pieces")) {
btInfo.setValue(key, tempBytes);
} else {
String tempStr = new String(tempBytes);
if (BitTorrentInfo.keyList.contains(tempStr)) {
key = tempStr;
if (tempStr.equals("announce-list")) {
btInfo.setAnnounceList(new LinkedList<String>());
} else if (tempStr.equals("info")) {
btInfo.setInfo(new Info());
} else if (tempStr.equals("files")) {
btInfo.getInfo().setFiles(new LinkedList<Files>());
btInfo.getInfo().getFiles().add(new Files());
} else if (tempStr.equals("length")) {
List<Files> tempFiles = btInfo.getInfo().getFiles();
if (tempFiles != null) {
if (tempFiles.isEmpty() || tempFiles.get(tempFiles.size() - 1).getLength() != 0) {
tempFiles.add(new Files());
}
}
} else if (tempStr.equals("md5sum")) {
List<Files> tempFiles = btInfo.getInfo().getFiles();
if (tempFiles != null) {
if (tempFiles.isEmpty() || tempFiles.get(tempFiles.size() - 1).getMd5sum() != null) {
tempFiles.add(new Files());
}
}
} else if (tempStr.equals("path")) {
List<Files> tempFilesList = btInfo.getInfo().getFiles();
if (tempFilesList.isEmpty()) {
Files files = new Files();
files.setPath(new LinkedList<String>());
tempFilesList.add(files);
} else {
Files files = tempFilesList.get(tempFilesList.size() - 1);
if (files.getPath() == null) {
files.setPath(new LinkedList<String>());
}
}
}
} else {
btInfo.setValue(key, tempStr);
}
}
break;
}
}
return btInfo;
}
public static void main(String[] args) throws Exception {
System.out.println(parse("E://【BT吧】[1080p]-鲁滨逊漂流记-Robinson Crusoe-1.73GB.torrent"));
}
}
beam申明
最外层BitTorrentInfo
package org.hanmeis.bt.bean;
import java.util.Arrays;
import java.util.List;
public class BitTorrentInfo {
public static List<String> keyList;
static{
String[] keys = {"announce", "announce-list", "creation date", "comment", "created by",
"info", "length", "md5sum", "name", "piece length","pieces", "files", "path"};
keyList = Arrays.asList(keys);
}
private String announce;
private List<String> announceList;
private long creationDate;
private String comment;
private String createBy;
private Info info;
public BitTorrentInfo() {
}
public void setValue(String key, Object value) throws Exception {
if(!keyList.contains(key)){
throw new Exception("not contains this key: " + key);
}else{
switch (key){
case "announce":this.setAnnounce(value.toString());break;
case "announce-list":this.getAnnounceList().add(value.toString());break;
case "creation date":this.setCreationDate(Long.parseLong(value.toString()));break;
case "comment":this.setComment(value.toString());break;
case "created by":this.setCreateBy(value.toString());break;
case "length":
List<Files> filesList1 = this.getInfo().getFiles();
if(filesList1 != null){
Files files = this.getInfo().getFiles().get(filesList1.size()-1);
files.setLength(Long.parseLong(value.toString()));
}else {
this.getInfo().setLength(Long.parseLong(value.toString()));
}
break;
case "md5sum":
List<Files> filesList2 = this.getInfo().getFiles();
if(filesList2 != null){
Files files = this.getInfo().getFiles().get(filesList2.size()-1);
files.setMd5sum(value.toString());
}else {
this.getInfo().setMd5sum(value.toString());
}
break;
case "name":
this.getInfo().setName(value.toString());
break;
case "piece length":
this.getInfo().setPiecesLength(Long.parseLong(value.toString()));
break;
case "pieces":
this.getInfo().setPieces((byte[])value);
break;
case "path":
List<Files> filesList3 = this.getInfo().getFiles();
Files files3 = filesList3.get(filesList3.size()-1);
files3.getPath().add(value.toString());
break;
}
}
}
}
第一层Info
package org.hanmeis.bt.bean;
import java.util.Arrays;
import java.util.List;
/**
* Created by zhao.wu on 2016/12/8.
*/
public class Info{
private String name;
private byte[] pieces;
private long piecesLength;
private long length;
private String md5sum;
private List<Files> files;
public Info() {
}
}
第二层Files
package org.hanmeis.bt.bean;
import java.util.List;
/**
* Created by zhao.wu on 2016/12/8.
*/
public class Files{
private long length;
private String md5sum;
private List<String> path;
public Files() {
}
}