使用C# Windows Forms 实现多线程文件下载管理器

在今天的博客中,我们将探讨如何使用C#和Windows Forms来创建一个简单的多线程文件下载管理器。这个文件下载管理器将从文本文件中读取下载链接,并使用多线程技术同时下载多个文件。

准备工作

首先,你需要确保你的开发环境中安装了Visual Studio,并创建了一个Windows Forms项目。此外,你还需要一个包含下载链接的文本文件,每个链接占一行,并可能包含其他相关信息(如文件名),这些信息之间使用分隔符(如“|”)分隔。

设计界面

在Windows Forms设计器中,你可以添加一个ListView控件来显示下载任务的列表,包括文件名、下载进度等信息。另外,你还需要一个按钮来触发下载过程。

编写代码

 定义了DownLoadFile类的实例dlf,用于处理下载操作。

 DownLoadFile dlf = new DownLoadFile();

btnTest_Click事件处理器 

 private void btnTest_Click(object sender, EventArgs e)
 {
     string[] lines = File.ReadAllLines("软件下载1.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 = @"F:\test";
             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();
 }

 事件处理器 SendMsgHander
 

事件处理器 SendMsgHander,用于响应从某个文件下载任务中传递出来的消息。从代码的内容可以看出,这个消息通常是一个自定义类型 DownMsg,其中包含了文件下载的状态信息(如开始、获取长度、下载中、完成、错误等)以及其他相关的数据(如下载的文件大小、已下载大小、速度等)。

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

完整代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Text;
using System.Windows.Forms;
using Gac;

namespace Demo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        DownLoadFile dlf = new DownLoadFile();
        private void btnTest_Click(object sender, EventArgs e)
        {
            string[] lines = File.ReadAllLines("软件下载1.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 = @"F:\test";
                    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();
        }

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

多线程下载管理模块

主要实现多线程的管理,根据给定的线程数ThreadNum进行处理。

 

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
 
namespace Gac
{
  
    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);
            }
        }
        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;
 
        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);
            }
        }
    }
}

 DownLoadFile

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
 
namespace Gac
{
  
    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);
            }
        }
        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;
        //public event doSendMsg;
        //public dlgSendMsg doSendMsg = null;
        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);
            }
        }
 
 
    }
   
}

DownloadProgressListener

using System;
using System.Collections.Generic;
using System.Text;
 
namespace Gac
{
    public class DownloadProgressListener : IDownloadProgressListener
    {
        private long presize=0;
        DownMsg downMsg = null;
        public DownloadProgressListener(DownMsg downmsg)
        {
            this.downMsg = downmsg;
            //this.id = id;
            //this.Length = Length;
        }
        public delegate void dlgSendMsg(DownMsg msg);
        public dlgSendMsg doSendMsg = null;
        public void OnDownloadSize(long size)
        {
            if (downMsg==null)
            {
                DownMsg downMsg = new DownMsg();
            }
 
 
            //下载速度
            if (downMsg.Size == 0)
            {
                downMsg.Speed = size;
            }
            else
            {
                downMsg.Speed = (float)(size - downMsg.Size);
                
            }
            if (downMsg.Speed == 0)
            {
                downMsg.Surplus = -1;
                downMsg.SurplusInfo = "未知";
            }
            else
            {
                downMsg.Surplus = ((downMsg.Length - downMsg.Size) / downMsg.Speed);
            }
            downMsg.Size = size; //下载总量
           
            if (size == downMsg.Length)
            {
                //下载完成
                downMsg.Tag = DownStatus.End;
                downMsg.SpeedInfo = "0 K";
                downMsg.SurplusInfo = "已完成";
            }
            else
            {
                //下载中
                downMsg.Tag = DownStatus.DownLoad;
                
 
            }
            
            
            if (doSendMsg != null) doSendMsg(downMsg);//通知具体调用者下载进度
        }
    }
    public enum DownStatus
    {
        Start,
        GetLength,
        DownLoad,
        End,
        Error
    }
    public class DownMsg
    {
        private int _Length = 0;
        private string _LengthInfo = "";
        private int _Id = 0;
        private DownStatus _Tag = 0;
        private long _Size = 0;
        private string _SizeInfo = "";
        private float _Speed = 0;
        private float _Surplus = 0;
        private string _SurplusInfo ="";
        private string _ErrMessage = "";
        private string _SpeedInfo = "";
        private double _Progress = 0;
 
        public int Length
        {
            get
            {
                return _Length;
            }
 
            set
            {
                _Length = value;
                LengthInfo = GetFileSize(value);
            }
        }
 
        public int Id
        {
            get
            {
                return _Id;
            }
 
            set
            {
                _Id = value;
            }
        }
        /// </summary>
        public DownStatus Tag
        {
            get
            {
                return _Tag;
            }
 
            set
            {
                _Tag = value;
            }
        }
 
        public long Size
        {
            get
            {
                return _Size;
            }
 
            set
            {
                _Size = value;
                SizeInfo = GetFileSize(value);
                if (Length >= value)
                {
                    Progress = Math.Round((double)value / Length * 100, 2);
                }
                else
                {
                    Progress = -1;
                }
            }
        }
 
        public float Speed
        {
        
        }
 
        //不过多久赘述,省略下方public代码...
    }
}

FileDownloader

从以下的FileDownloader类代码片段中,可以看到这是一个用于多线程文件下载的类的部分实现

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.IO;
using System.Net;
 
namespace Gac
{
    public class FileDownloader
    {
       
        private long downloadSize = 0;
      
        private long fileSize = 0;
       
        private DownloadThread[] threads;
    
        private string saveFile;
       
        public Dictionary<int, long> data = new Dictionary<int, long>();
      
        private long block;
       
        private String downloadUrl;
    
        public int getThreadSize()
        {
            return threads.Length;
        }
       
        public long getFileSize()
        {
            return fileSize;
        }
       
        public void append(long size)
        {
            lock (this)  //锁定同步..........
            {
                downloadSize += size;
            }
 
        }
       
        public void update(int threadId, long pos)
        {
            if (data.ContainsKey(threadId))
            {
                this.data[threadId] = pos;
            }
            else
            {
                this.data.Add(threadId, pos);
            }
        }
 
       
        public FileDownloader(string downloadUrl, string fileSaveDir,string filename="", int threadNum=3)
        {
            try
            {
                if (string.IsNullOrEmpty(filename))
                {
                    //省略...
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                throw new Exception("无法连接下载地址");
            }
        }
 
        public long download(IDownloadProgressListener listener)
        {
            try
            {
                using (FileStream fstream = new FileStream(this.saveFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite))
                {
                    if (this.fileSize > 0) fstream.SetLength(this.fileSize);
                    fstream.Close();
                }
                if (this.data.Count != this.threads.Length)
                {
                    this.data.Clear();
                    for (int i = 0; i < this.threads.Length; i++)
                    {
                        this.data.Add(i + 1, 0);//初始化每条线程已经下载的数据长度为0
                    }
                }
                for (int i = 0; i < this.threads.Length; i++)
                {//开启线程进行下载
                    long downLength = this.data[i + 1];
                    if (downLength < this.block && this.downloadSize < this.fileSize)
                    {
                        //判断线程是否已经完成下载,否则继续下载	
                    }
                    else
                    {
                        this.threads[i] = null;
                    }
                }
                bool notFinish = true;//下载未完成
                while (notFinish)
                {// 循环判断所有线程是否完成下载
                    Thread.Sleep(900);
                    notFinish = false;//假定全部线程下载完成
                    for (int i = 0; i < this.threads.Length; i++)
                    {
                        if (this.threads[i] != null && !this.threads[i].isFinish())
                        {//如果发现线程未完成下载
                            notFinish = true;//设置标志为下载没有完成
                            if (this.threads[i].getDownLength() == -1)
                            {//如果下载失败,再重新下载
                                this.threads[i] = new DownloadThread(this, downloadUrl, this.saveFile, this.block, this.data[i + 1], i + 1);
                                this.threads[i].ThreadRun();
                            }
                        }
                    }
                    if (listener != null)
                    {
                        listener.OnDownloadSize(this.downloadSize);//通知目前已经下载完成的数据长度
                        Console.WriteLine(this.downloadSize);
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                throw new Exception("下载文件失败");
            }
            return this.downloadSize;
        }
    }
}

结果展示

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值