Torrent文件采用BEncode编码方式(详细介绍见参考文献6),为了读取Torrent文件中的文件信息,需要解析Torrent文件并获取每个文件的详细路径和名称。
在GitHub中查找Torrent文件解析程序,通过TorrentFileChecker项目(参考文献4)找到一个专门编码和解码BEncode格式文件的C#开源项目BencodeNET(参考文献5),该项目提供有BencodeParser类和Torrent类解析Torrent文件,其中Torrent类的定义如下所示,本文中主要读取该类中的Files属性。
public class Torrent : BObject
{
public Torrent();
public virtual bool IsPrivate { get; set; }
public virtual string PiecesAsHexString { get; set; }
public virtual byte[] Pieces { get; set; }
public virtual long PieceSize { get; set; }
public Encoding Encoding { get; set; }
public virtual string CreatedBy { get; set; }
public virtual string Comment { get; set; }
public virtual DateTime? CreationDate { get; set; }
public virtual string DisplayNameUtf8 { get; }
public virtual string DisplayName { get; }
public virtual TorrentFileMode FileMode { get; }
public virtual MultiFileInfoList Files { get; set; }
public virtual SingleFileInfo File { get; set; }
public virtual IList<IList<string>> Trackers { get; set; }
public virtual long TotalSize { get; }
public virtual BDictionary ExtraFields { get; set; }
public string OriginalInfoHash { get; protected set; }
public byte[] OriginalInfoHashBytes { get; protected set; }
public virtual int NumberOfPieces { get; }
public override bool Equals(object other);
public override int GetHashCode();
public virtual string GetInfoHash();
public virtual byte[] GetInfoHashBytes();
public virtual string GetMagnetLink(MagnetLinkOptions options = MagnetLinkOptions.IncludeTrackers);
public override int GetSizeInBytes();
public virtual BDictionary ToBDictionary();
protected virtual BDictionary CreateInfoDictionary(Encoding encoding);
protected override void EncodeObject(System.IO.Pipelines.PipeWriter writer);
protected override void EncodeObject(Stream stream);
protected override System.Threading.Tasks.ValueTask<System.IO.Pipelines.FlushResult> EncodeObjectAsync(System.IO.Pipelines.PipeWriter writer, CancellationToken cancellationToken);
public static bool operator ==(Torrent first, Torrent second);
public static bool operator !=(Torrent first, Torrent second);
}
新建Winform项目,并添加BencodeNET程序集引用。为测试Torrent类解析复杂Torrent文件的情况,在网上找了老友记十季合集的Torrent文件,并调用以下代码解析文件:/font>
OpenFileDialog ofd = new OpenFileDialog();
ofd.Multiselect = false;
ofd.Filter = "Torrent文件|*.Torrent";
if (ofd.ShowDialog(this) != DialogResult.OK)
{
return;
}
Torrent infoTorr = new BencodeParser().Parse<Torrent>(ofd.FileName);
ConstructFileTree(infoTorr);
中断代码执行,并在快速监视中查看infoTorr对象的内容,可以看到Torrent文件中的文件信息都已解析并保存在Files属性中。
进一步查看Files属性值,从下图可以看出,Files集合中的每条数据对应一个文件,其中FileName保存文件名,FullPath属性保存文件的完整路径,而Path集合记录文件的每一级路径,集合的最后一个值为文件名。
基于上述属性,构建树形文件结构,将相同目录下的文件置于一块,其主要代码如下:
private TreeNode FindNode(TreeNode tnParent, string nodeText)
{
if (tnParent.Nodes[nodeText] == null)
{
TreeNode tnNew = new TreeNode(nodeText);
tnNew.Name = nodeText;
tnParent.Nodes.Add(tnNew);
}
return tnParent.Nodes[nodeText];
}
private void ConstructFileTree(Torrent infoTor)
{
treeView1.Nodes.Clear();
TreeNode root = new TreeNode("种子文件");
treeView1.Nodes.Add(root);
TreeNode tnNow = null;
foreach (MultiFileInfo f in infoTor.Files)
{
tnNow = root;
if (f.Path.Count > 1)
{
for (int i = 0; i < f.Path.Count - 1; i++)
{
tnNow = FindNode(tnNow, f.Path[i]);
}
}
if (tnNow.Tag == null)
{
tnNow.Tag = new List<string>();
}
(tnNow.Tag as List<string>).Add(f.Path.Last());
}
}
程序运行效果如下图所示:
参考文献:
[1]https://blog.csdn.net/zgke/article/details/2868282
[2]https://www.cnblogs.com/a14907/p/7593299.html
[3]https://github.com/a14907/AConsoleAppForFun
[4]https://github.com/hironpan/TorrentFileChecker
[5]https://github.com/Krusen/BencodeNET
[6]https://blog.csdn.net/weixin_34153893/article/details/86053255