目录
目录
1.作业内容
2.作业要求
3.主要思路
1)窗体和组件初始化
2)下载管理器实例化
3)按钮点击事件处理
4)窗体加载事件处理
5)下载消息处理
4.主要难点
1)多线程管理:
2) UI更新:
3) 错误处理:
4) 资源管理:
5) 用户体验:
5.不足及改进
参考:
6.代码展示
代码仓库
7.运行结果
1.作业内容
通过c#实现一个基本的多线程文件下载器,用于从一个文本文件中读取下载链接,并启动多线程下载,可以在Windows窗体应用程序中使用。同时,也可以更新UI显示文件下载内容和进度情况。
- 并发下载
- 网络连接
- ...
2.作业要求
请以博客方式提交作业,博客内容需要对代码行进行讲解,阐述设计的主要思路与难点。 | √ |
请注意,如果想达到博客网站如csdn的优质博文的质量分,需要对博客进行详细描述。 | √ |
提交方式为提交博客发布地址,并附上本博文的质量分。 | √ |
3.主要思路
由于核心代码已有,本人只是在此基础上进行修改,理解其核心原理和过程
1)窗体和组件初始化
- `Form1()` 构造函数:初始化窗体,并调用 `InitializeComponent()` 方法,后者由WinForms设计器自动生成,用于设置窗体上的控件。
public Form1()
{
InitializeComponent();
}
2)下载管理器实例化
- `DownLoadFile dlf = new DownLoadFile();`:创建 `DownLoadFile` 类的实例,该类负责管理下载任务。
DownLoadFile dlf = new DownLoadFile();
3)按钮点击事件处理
- - `btnTest_Click`:当用户点击界面上的某个按钮时触发此事件。
- - 读取文本文件 `"下载文件.txt"` 中的每一行,每行包含一个文件名和一个URL,它们通过 `|` 分隔。
- - 对每行进行分割,提取文件名和URL。
- - 使用 `Uri.EscapeUriString` 对URL进行编码,确保URL在传输过程中的安全性。
- - 将文件下载存放路径设置为 `dir` 变量。
- - 在列表视图 `listView1` 中为每个下载任务添加一个项,并设置初始状态。
- - 调用 `dlf.AddDown` 方法添加下载任务,传入下载链接、存放目录、任务索引和索引的字符串表示。
- - 调用 `dlf.StartDown` 开始下载任务。
private void btnTest_Click(object sender, EventArgs e)
{
string[] lines = File.ReadAllLines("D:\\table\\作业\\windows\\作业\\作业三\\DownLoadFile\\下载文件.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 = line[0];
//string filename = Path.GetFileName(path);
string dir = @"D:\\table\作业\windows\作业\作业三\DownLoadFile\文件下载存放处";
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();
}
4)窗体加载事件处理
- - `Form1_Load`:在窗体加载时设置下载器的线程数,并注册下载过程中的消息处理事件 `SendMsgHander`。
private void Form1_Load(object sender, EventArgs e)
{
dlf.ThreadNum = 3;//线程数,不设置默认为3
dlf.doSendMsg += SendMsgHander;//下载过程处理事件
}
5)下载消息处理
- - `SendMsgHander`:根据下载过程中的不同状态更新UI。
- - `DownStatus.Start`:下载开始时更新状态。
- - `DownStatus.GetLength`:获取文件长度时更新状态。
- - `DownStatus.End` 和 `DownStatus.DownLoad`:下载过程中和下载结束时更新进度、速度和剩余时间。
- - `DownStatus.Error`:下载出错时更新错误信息。
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; } }
4.主要难点
1)多线程管理:
正确地管理多个下载线程,确保它们不会相互干扰,同时高效地利用系统资源。
2) UI更新:
在多线程环境中安全地更新UI,因为UI控件只能通过创建它们的线程(通常是主线程)进行操作。这里使用了 `Invoke` 方法来确保在主线程上更新UI。
3) 错误处理:
在下载过程中可能会遇到各种错误,如网络问题、文件写入权限问题等。我们需要能够处理这些错误,并给用户适当的反馈。
4) 资源管理:
确保所有资源(如文件流、网络连接)在使用后都能正确关闭和释放,防止资源泄露。
5) 用户体验:
提供清晰的进度指示和错误信息,使用户能够了解下载状态和问题。
5.不足及改进
对下载文件的命名进行优化,下载生成文件具有良好的可阅读性,同时能对其后缀进行自动添加。
能否由用户控制下载哪几个文件,而不是全部下载了。
参考:
https://www.cnblogs.com/jianzhan/p/7137485.html
6.代码展示
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("D:\\table\\作业\\windows\\作业\\作业三\\DownLoadFile\\下载文件.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 = line[0];
//string filename = Path.GetFileName(path);
string dir = @"D:\\table\作业\windows\作业\作业三\DownLoadFile\文件下载存放处";
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;
}
}
}
}
代码仓库
https://github.com/Tiansky9/sky.git