目标:利用C#语言在Windows窗体程序中编写一个程序来实现文件并发下载。
一、问题分析
1.需求理解
首先,我们需要明确并发下载的需求。并发下载意味着程序能够同时处理多个下载任务,每个任务可能从不同的服务器或相同的服务器上的不同文件路径下载文件。
2.具体思路
定义一个类DownLoadFile
,通过读取文件列表、初始化下载任务、并发执行下载并更新UI状态,程序能够同时处理多个下载任务,具体的并发控制和下载逻辑在DownLoadFile
类中实现。
二、准备工作
1.布置控件
要实现这个功能,我们需要两个控件,分别为listview和一个button。效果如下:
2.编辑listview
点击下图蓝色字的第二项即可编辑列
点击到这个页面可以依据自己的需求进行添加列数、修改列名、修改列的参数等操作。
最后的效果可以参考下图:
三、代码实现
1.定义类
DownLoadFile dlf = new DownLoadFile();
定义了一个名为DownLoadFile的类,这个类的作用通常是为了封装与文件下载相关的功能。
2.button(btnTest_Click)
private void btnTest_Click(object sender, EventArgs e)
{
string[] lines = File.ReadAllLines("//下载软件的信息与下载链接");
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 = @"//下载至电脑中的路径";
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();
}
2.1读取文件内容
使用 File.ReadAllLines 方法从 文件中读取所有行,并将它们存储在一个字符串数组 lines 中。
2.2遍历文件行
使用 for 循环遍历 lines 数组中的每一行。
2.3解析每一行数据
使用 String.Split 方法,按 "|" 符号将每行拆分为多个部分,并移除空的部分。拆分结果存储在 line 数组中。
检查 line 数组的长度是否为 2,以确保每行都包含两个字段。
2.4处理每行数据
(1)将 line[1]进行 URI 转义处理,并存储在 path 变量中。
(2)使用 Path.GetFileName 方法从 path 中提取文件名,并存储在 filename 变量中。
(3)定义一个目标目录 dir,创建一个新的 ListViewItem 对象,并使用从 line 数组和其他计算值中获取的多个字段来初始化它。这个 ListViewItem 对象被添加到 listView1 控件的 Items 集合中。
(4)获取新添加的 ListViewItem 的索引值,并将其存储在 id 变量中。
(5)调用 dlf.AddDown 方法,该方法接收下载链接、目标目录、ID 和 ID 的字符串表示作为参数。
2.5启动下载
调用 dlf.StartDown 方法来启动下载任务。这个方法的具体实现没有在代码中给出,但我们可以假设它会开始处理之前通过 AddDown 方法添加的所有下载任务。
3.Form_load1
private void Form1_Load(object sender, EventArgs e)
{
dlf.ThreadNum = 3;//线程数,不设置默认为3
dlf.doSendMsg += SendMsgHander;//下载过程处理事件
}
(1)当Form1窗体加载时,设置了下载任务的线程数为3。
(2)同时,注册了一个事件处理器SendMsgHander来处理下载过程中可能触发的doSendMsg事件。
4.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;
}
}
这段代码是一个事件处理器SendMsgHander,它用于处理从文件下载任务中发送过来的消息:
使用了一个switch语句来根据msg.Tag的值执行不同的操作,通过列举类DownStatus的成员来处理不同状态。
这段代码还使用了InVoke方法确保线程安全,由于这个事件处理器可能是由后台下载线程调用的,而listView1是在UI线程上创建的,因此需要使用Invoke方法来确保对listView1的操作是在UI线程上执行的。代码中多次使用了Invoke方法来确保线程安全。
四、代码实现
总代码:
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("//下载软件的信息与下载链接");
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 = @"//下载至电脑中的路径";
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;
}
}
}
}
点击测试:
下载完成!