在这篇博客中,我们将详细介绍如何使用C#实现一个多线程文件下载的项目。通过这个项目,你可以学习如何读取文件列表、利用多线程进行文件下载以及更新UI显示下载进度。我们将逐步讲解每一部分的代码及其功能。
项目概述
该项目主要分为两个部分:
- Windows Forms 应用程序:用于显示下载进度并处理用户交互。
- 下载管理类:负责处理文件下载的核心逻辑,包括多线程管理。
一、Windows Forms 应用程序
首先,我们创建一个名为 Form1
的 Windows Forms 窗体应用程序。以下是关键代码和其解释。
1. 初始化组件和下载管理对象
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
DownLoadFile dlf = new DownLoadFile();
Form1
类的构造函数中调用 InitializeComponent
方法初始化组件,同时创建一个 DownLoadFile
类的实例,用于管理下载任务。
2. 处理按钮点击事件
private void btnTest_Click(object sender, EventArgs e)
{
string[] lines = File.ReadAllLines("C:\\Users\\26807\\Desktop\\DownLoadFile\\download\\text1.txt");
for (int i = 0; i < lines.Length; i++)
{
string[] line = lines[i].Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries);
if (line.Length == 2)
{
string path = Uri.EscapeUriString(line[1]);
string filename = Path.GetFileName(path);
string dir = @"C:\Users\26807\Desktop\DownLoadFile\download\downloadfiles";
ListViewItem item = listView1.Items.Add(new ListViewItem(new string[] { (listView1.Items.Count + 1).ToString(), filename, "0", "0", "0%", "0", "0", DateTime.Now.ToString(), "等待中", line[1] }));
int id = item.Index;
dlf.AddDown(path, dir, id, id.ToString());
}
}
dlf.StartDown();
}
当用户点击按钮时,程序会读取 text1.txt
文件的内容。文件的每一行包含一个文件名和一个下载链接,通过 |
分隔。程序将这些信息解析并添加到 ListView
控件中显示,同时将下载任务添加到 DownLoadFile
实例中。
3. 窗体加载事件
private void Form1_Load(object sender, EventArgs e)
{
dlf.ThreadNum = 3; // 线程数,不设置默认为3
dlf.doSendMsg += SendMsgHander; // 下载过程处理事件
}
在窗体加载时,设置下载线程数并将 SendMsgHander
方法订阅到 doSendMsg
事件,用于处理下载过程中的消息。
4. 处理下载过程中的消息
private void SendMsgHander(DownMsg msg)
{
switch (msg.Tag)
{
case DownStatus.Start:
this.Invoke((MethodInvoker)delegate ()
{
listView1.Items[msg.Id].SubItems[8].Text = "开始下载";
listView1.Items[msg.Id].SubItems[7].Text = DateTime.Now.ToString();
});
break;
case DownStatus.GetLength:
this.Invoke((MethodInvoker)delegate ()
{
listView1.Items[msg.Id].SubItems[3].Text = msg.LengthInfo;
listView1.Items[msg.Id].SubItems[8].Text = "连接成功";
});
break;
case DownStatus.End:
case DownStatus.DownLoad:
this.Invoke(new MethodInvoker(() =>
{
this.Invoke((MethodInvoker)delegate ()
{
listView1.Items[msg.Id].SubItems[2].Text = msg.SizeInfo;
listView1.Items[msg.Id].SubItems[4].Text = msg.Progress.ToString() + "%";
listView1.Items[msg.Id].SubItems[5].Text = msg.SpeedInfo;
listView1.Items[msg.Id].SubItems[6].Text = msg.SurplusInfo;
if (msg.Tag == DownStatus.DownLoad)
{
listView1.Items[msg.Id].SubItems[8].Text = "下载中";
}
else
{
listView1.Items[msg.Id].SubItems[8].Text = "下载完成";
}
Application.DoEvents();
});
}));
break;
case DownStatus.Error:
this.Invoke((MethodInvoker)delegate ()
{
listView1.Items[msg.Id].SubItems[6].Text = "失败";
listView1.Items[msg.Id].SubItems[8].Text = msg.ErrMessage;
Application.DoEvents();
});
break;
}
}
该方法通过事件处理器接收下载状态的更新并更新 ListView
中对应任务的显示信息,包括下载进度、速度、剩余时间和状态。
二、下载管理类
DownLoadFile
类是负责下载文件的核心类。以下是关键代码和其解释。
1. 类的定义与初始化
public class DownLoadFile
{
public int ThreadNum = 3;
List<Thread> list = new List<Thread>();
public DownLoadFile()
{
doSendMsg += Change;
}
private void Change(DownMsg msg)
{
if (msg.Tag == DownStatus.Error || msg.Tag == DownStatus.End)
{
StartDown(1);
}
}
初始化下载管理类时,设置默认的下载线程数为3,并将 Change
方法订阅到 doSendMsg
事件中,用于处理下载过程中发生错误或完成后的动作。
2. 添加下载任务
public void AddDown(string DownUrl, string Dir, int Id = 0, string FileName = "")
{
Thread tsk = new Thread(() =>
{
download(DownUrl, Dir, FileName, Id);
});
list.Add(tsk);
}
添加下载任务时,创建一个新线程并将其加入到线程列表中。每个线程负责一个文件的下载任务。
3. 开始下载任务
public void StartDown(int StartNum = 3)
{
for (int i2 = 0; i2 < StartNum; i2++)
{
lock (list)
{
for (int i = 0; i < list.Count; i++)
{
if (list[i].ThreadState == System.Threading.ThreadState.Unstarted || list[i].ThreadState == ThreadState.Suspended)
{
list[i].Start();
break;
}
}
}
}
}
开始下载任务时,根据指定的线程数启动未开始或挂起的线程。
4. 下载文件的实现
private void download(string path, string dir, string filename, int id = 0)
{
try
{
DownMsg msg = new DownMsg();
msg.Id = id;
msg.Tag = 0;
doSendMsg(msg);
FileDownloader loader = new FileDownloader(path, dir, filename, ThreadNum);
loader.data.Clear();
msg.Tag = DownStatus.Start;
msg.Length = (int)loader.getFileSize(); ;
doSendMsg(msg);
DownloadProgressListener linstenter = new DownloadProgressListener(msg);
linstenter.doSendMsg = new DownloadProgressListener.dlgSendMsg(doSendMsg);
loader.download(linstenter);
}
catch (Exception ex)
{
DownMsg msg = new DownMsg();
msg.Id = id;
msg.Length = 0;
msg.Tag = DownStatus.Error;
msg.ErrMessage = ex.Message;
doSendMsg(msg);
Console.WriteLine(ex.Message);
}
}
该方法负责具体的文件下载过程,包括初始化下载器、获取文件大小、启动下载并监听下载进度。如果发生错误,会触发 doSendMsg
事件并传递错误消息。
最后这是我运行的结果:
项目总结
通过本项目,我们实现了一个简单但功能强大的多线程文件下载器。项目主要包含两个部分:Windows Forms 应用程序和下载管理类。通过读取下载任务文件并利用多线程下载,我们能够高效地下载多个文件,并通过UI实时显示下载进度。
希望这篇博客能帮助你理解如何使用C#实现多线程文件下载。如果有任何问题或建议,欢迎在评论区留言讨论!