基于 C# 的多线程文件下载器

1.前言

在许多软件和应用程序中,需要从网络上下载文件并更新到本地。如果文件较大,或者网络不太稳定,单线程下载可能会耗费大量时间和带宽。因此,开发一个支持多线程下载的文件下载器,可以提高下载效率,增强用户体验。本文将介绍一个基于 C# 的多线程文件下载器的实现。

2.主要功能

  1. 支持多线程下载:用户可以设置线程数,默认为 3 个线程同时下载。当某个线程下载失败时,会自动启动新的线程继续下载。
  2. 提供下载进度显示:下载过程中,会实时显示已下载大小、下载速度、预计剩余时间等信息。
  3. 支持断点续传:当下载过程中断时,可以从上次中断的位置继续下载,不需要重新下载整个文件。

3.界面设计

其中使用了以下 Windows Forms 控件

  1. ListView:

    • 用于显示下载任务的详细信息,如文件名、下载进度、下载速度等。
    • 在代码中, listView1 对象被用来操作 ListView 控件,如添加新的 ListViewItem 等。
  2. Button:

    • 在 Form1 窗体中,有一个名为 btnTest 的按钮,用于启动下载任务。
    • 按钮的 Click 事件被用来触发下载逻辑。
  3. 事件处理:

    • Form1 类中定义了一个名为 SendMsgHander 的方法,用于处理 DownLoadFile 类中发送的下载状态更新消息。
    • 该方法使用 Invoke 方法来确保在 UI 线程中更新 ListView 控件的内容。
  4. 文件操作:

    • 在 btnTest_Click 方法中,代码读取了名为 "软件下载1.txt" 的文件,并解析其中的下载任务信息。
    • 然后将这些任务信息添加到 ListView 控件中,并启动下载任务。

4.代码实现 

4.1.界面代码

private void btnTest_Click(object sender, EventArgs e)
{
    // 读取 "软件下载1.txt" 文件中的所有行
    string[] lines = File.ReadAllLines("软件下载1.txt");
    // 遍历每一行数据
    for (int i = 0; i < lines.Length; i++)
    {
        // 将每一行数据按照 '|' 分隔符分割成数组
        string[] line = lines[i].Split(new string[] { "|" }, StringSplitOptions.RemoveEmptyEntries);
        // 如果分割后的数组长度为 2
        if (line.Length == 2)
        {
            // 获取下载路径,并对其进行 URI 编码
            string path = Uri.EscapeUriString(line[1]);
            // 从下载路径中获取文件名
            string filename = Path.GetFileName(path);
            // 设置下载目录为 "E:\test"
            string dir = @"E:\test";
            // 在 ListView 控件中添加一个新的项目
            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 对象中
            dlf.AddDown(path, dir, id, id.ToString());
        }
    }
    // 启动下载任务
    dlf.StartDown();
}

这个代码片段主要负责解析文件中的下载任务信息,并将其添加到 ListView 控件中,同时将下载任务交给 dlf 对象进行处理。这种方式可以方便地在 Windows 窗体中展示和管理多个下载任务。

 private void Form1_Load(object sender, EventArgs e)
 {
     dlf.ThreadNum = 3;//线程数,不设置默认为3
     dlf.doSendMsg += SendMsgHander;//下载过程处理事件
 }

设置线程数和下载过程处理事件

private void SendMsgHander(DownMsg msg)
{
    // 根据下载状态标签 (msg.Tag) 进行不同的处理
    switch (msg.Tag)
    {
        case DownStatus.Start:
            // 在 UI 线程中更新 ListView 项的开始下载状态和开始时间
            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:
            // 在 UI 线程中更新 ListView 项的连接成功状态和连接长度信息
            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:
            // 在 UI 线程中更新 ListView 项的下载进度、下载速度、剩余时间和下载状态
            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:
            // 在 UI 线程中更新 ListView 项的下载失败状态和错误信息
            this.Invoke((MethodInvoker)delegate ()
            {
                listView1.Items[msg.Id].SubItems[6].Text = "失败";
                listView1.Items[msg.Id].SubItems[8].Text = msg.ErrMessage;
                Application.DoEvents();
            });
            break;
    }
}

总的来说,这段代码实现了将下载过程中的各种状态信息更新到 listView1 控件中,为用户提供了一个直观的下载进度展示界面。

4.2功能函数代码

private void Change(DownMsg msg)
{
    // 如果下载状态是"错误"或"结束",则启动下一个下载任务
    if (msg.Tag == DownStatus.Error || msg.Tag == DownStatus.End)
    {
        StartDown(1);
    }
}

public void AddDown(string DownUrl, string Dir, int Id = 0, string FileName = "")
{
    // 创建一个新的下载任务线程,并添加到任务列表中
    Thread tsk = new Thread(() =>
    {
        download(DownUrl, Dir, FileName, Id);
    });
    list.Add(tsk);
}

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;
                }
            }
        }
    }
}

这段代码实现了一个简单的下载任务管理器,可以根据下载状态自动启动新的下载任务,并且可以同时运行多个下载任务。这种设计可以提高下载效率,但需要注意线程安全问题,以及可能产生的资源竞争和消耗等问题。

public delegate void dlgSendMsg(DownMsg msg);
public event dlgSendMsg doSendMsg;
// 'doSendMsg' 事件用于通知订阅者下载操作的进度情况。
private void download(string path, string dir, string filename, int id = 0)
{
    try
    {
        // 创建一个新的DownMsg对象来保存下载状态
        DownMsg msg = new DownMsg();
        msg.Id = id;
        msg.Tag = 0; // 初始状态
        // 通知订阅者下载已经开始
        doSendMsg(msg);
        // 创建一个FileDownloader实例来处理实际的文件下载
        FileDownloader loader = new FileDownloader(path, dir, filename, ThreadNum);
        loader.data.Clear();
        // 更新DownMsg对象的开始状态和文件大小
        msg.Tag = DownStatus.Start;
        msg.Length = (int)loader.getFileSize();
        doSendMsg(msg);
        // 创建一个DownloadProgressListener来监控下载进度
        DownloadProgressListener linstenter = new DownloadProgressListener(msg);
        linstenter.doSendMsg = new DownloadProgressListener.dlgSendMsg(doSendMsg);
        // 开始下载过程
        loader.download(linstenter);
    }
    catch (Exception ex)
    {
        // 如果在下载过程中发生异常,创建一个新的DownMsg对象,并设置错误状态和错误消息
        DownMsg msg = new DownMsg();
        msg.Id = id;
        msg.Length = 0;
        msg.Tag = DownStatus.Error;
        msg.ErrMessage = ex.Message;       
        // 通知订阅者下载出现错误
        doSendMsg(msg);
        // 将错误消息打印到控制台
        Console.WriteLine(ex.Message);
    }
}

该代码实现了一个文件下载功能,可以通知订阅者下载的进度和状态。它使用 FileDownloader 类来处理实际的文件下载过程,并使用 DownloadProgressListener 类来监控下载进度。当下载开始、完成或出现错误时,它会触发 doSendMsg 事件,通知订阅者相关信息。订阅者可以通过订阅 doSendMsg 事件来获取下载的实时状态,并进行相应的处理。

5.运行结果

注意:序号4的失败是故意设计的 

这是我下载的空白文档,下载位置就是 string dir = @"E:\test";下载地址存放在 string[] lines = File.ReadAllLines("软件下载1.txt");

6.github仓库地址

https://github.com/zhiyinnitaimei6/-C-

7.总结

这个 C# 程序实现了一个简单的文件下载功能。它从一个文本文件中读取下载链接和文件名,并将这些信息添加到一个 ListView 控件中。当用户点击"测试"按钮时,程序会启动多线程下载器,并在 ListView 中实时显示下载进度,包括文件大小、下载速度、剩余时间等信息。如果下载出现错误,程序能够自动重新开始下载。这个程序还可以自定义线程数,以适应不同的网络环境。总的来说,这个程序实现了一个功能完整的文件下载功能,展示了使用 C# 进行多线程编程和 Windows 窗体编程的基本技能。通过这个实验,可以学习到如何利用 C# 的各种类库和控件来开发桌面应用程序,以及如何处理文件下载等常见的应用场景。

  • 57
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《CLR via C#》是一本经典的.NET开发书籍,由Jeffrey Richter编写。这本书深入介绍了.NET框架中的CLR(公共语言运行时)。CLR是.NET应用程序的基础,它负责管理内存、执行代码、处理异常、安全性等方面。 《CLR via C#》一书首先详细介绍了通用类型系统(CTS),它是CLR的核心。CTS定义了如何创建和使用类型及其成员。读者可以学习如何声明和定义类、结构体、接口、枚举等,以及如何使用属性、方法、字段等来操作这些类型。 本书还介绍了.NET的垃圾回收机制,解释了垃圾回收如何跟踪和回收不再使用的对象,以及如何使用终结来执行资源清理操作。读者可以理解垃圾回收的工作原理,以及如何最大程度地优化资源利用。 另外,《CLR via C#》也涵盖了多线程和同步的主题。在多核和多处理时代,充分利用多线程的优势是非常重要的。通过本书的指导,读者可以学习到如何创建和管理线程,以及如何使用各种同步机制,如锁、信号量、事件等,来确保多线程的正确执行。 此外,书中还探讨了异常处理、安全性和应用程序域等主题。这些都是.NET开发中不可或缺的内容。 总之,《CLR via C#》是一本帮助开发人员深入了解.NET框架和CLR的重要书籍。无论是初学者还是有经验的开发人员,都可以通过阅读本书来提升自己的.NET开发技能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值