一、程序设计要求
- 能够在下载过程中显示进度信息(如总大小、已下载大小、进度、下载速度、剩余大小、剩余时间、状态、下载的网址等)。
- 支持从指定的URL下载文件。
- 支持多线程并发下载文件。
- 提供友好的用户界面(UI)来下载。
- 具有良好的可扩展性,能够方便地添加新功能或修改现有功能。
- 代码结构清晰,易于理解和维护。
二、程序设计思路
2.1用户界面(UI)设计
- 设计一个直观易用的图形用户界面,包含以下组件:
- 序号:下载文件的序号
- 文件名:下载的文件名称
- 总大小:所下载文件的总大小
- 已完成:显示已经完成的大小
- 进度:显示下载进度。
- 速度:显示下载速度。
- 剩余:显示剩余的大小。
- 时间:显示开始下载时间。
- 状态:显示当前下载的状态(如“正在下载”、“已暂停”、“已完成”等)。
- 网址:显示所下载的网站地址。
2.2下载流程设计
- 当用户点击下载按钮时,开始执行以下流程:
- 验证输入的URL是否有效。
- 发送HTTP GET请求到指定的URL,获取文件的元数据信息(包括文件总大小)。
- 根据获取到的文件总大小,初始化进度条和进度信息面板。
- 创建一个下载任务,开始接收服务器的响应数据,并将数据写入到本地的文件中。
- 在接收数据的过程中,实时更新进度条、已下载大小、下载速度、剩余大小等信息。
- 估算剩余时间(可以使用已下载数据量和平均下载速度来计算)。
- 当所有数据接收完毕后,标记下载状态为“已完成”,并关闭相关的下载资源。
2.3错误处理设计
- 在下载过程中,可能会遇到各种错误,如网络连接中断、下载路径有误、URL无效等。
- 当发生错误时,停止下载任务,并弹出错误信息。
三、窗体设计
根据设计思路设计窗体如下:
其中用到的主要控件及功能如下:
button:点击进行多线程下载任务。
listview:用于显示所下载文件的信息,将下载过程可视化。
四、代码实现
为了方便实现,将该程序分为4个大模块进行实现。
文件下载模块:主要实现连接下载地址和文件下载的处理
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.IO;
using System.Net;
namespace Gac
{
public class FileDownloader
{
/// <summary>
/// 已下载文件长度
/// </summary>
private long downloadSize = 0;
/// <summary>
/// 原始文件长度
/// </summary>
private long fileSize = 0;
/// <summary>
/// 线程数
/// </summary>
private DownloadThread[] threads;
/// <summary>
/// 本地保存文件
/// </summary>
private string saveFile;
/// <summary>
/// 缓存各线程下载的长度
/// </summary>
public Dictionary<int, long> data = new Dictionary<int, long>();
/// <summary>
/// 每条线程下载的长度
/// </summary>
private long block;
/// <summary>
/// 下载路径
/// </summary>
private String downloadUrl;
/// <summary>
/// 获取线程数
/// </summary>
/// <returns> 获取线程数</returns>
public int getThreadSize()
{
return threads.Length;
}
/// <summary>
/// 获取文件大小
/// </summary>
/// <returns>获取文件大小</returns>
public long getFileSize()
{
return fileSize;
}
/// <summary>
/// 累计已下载大小
/// </summary>
/// <param name="size">累计已下载大小</param>
public void append(long size)
{
lock (this) //锁定同步..........
{
downloadSize += size;
}
}
/// <summary>
/// 更新指定线程最后下载的位置
/// </summary>
/// <param name="threadId">threadId 线程id</param>
/// <param name="pos">最后下载的位置</param>
public void update(int threadId, long pos)
{
if (data.ContainsKey(threadId))
{
this.data[threadId] = pos;
}
else
{
this.data.Add(threadId, pos);
}
}
/// <summary>
/// 构建下载准备,获取文件大小
/// </summary>
/// <param name="downloadUrl">下载路径</param>
/// <param name="fileSaveDir"> 文件保存目录</param>
/// <param name="threadNum">下载线程数</param>
public FileDownloader(string downloadUrl, string fileSaveDir,string filename="", int threadNum=3)
{
try
{
if (string.IsNullOrEmpty(filename))
{
filename = Uri.UnescapeDataString(Path.GetFileName(downloadUrl));//获取文件名称 uri 解码中文字符
}
//构建http 请求
this.downloadUrl = downloadUrl;
if (!Directory.Exists(fileSaveDir)) Directory.CreateDirectory(fileSaveDir);
this.threads = new DownloadThread[threadNum];
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(downloadUrl);
request.Referer = downloadUrl.ToString();
request.Method = "GET";
request.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; .NET CLR 2.0.1124)";
request.ContentType = "application/octet-stream";
request.Accept = "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*";
request.Timeout = 20 * 1000;
request.AllowAutoRedirect = true;
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
if (response.StatusCode == HttpStatusCode.OK)
{
this.fileSize = response.ContentLength;//根据响应获取文件大小
if (this.fileSize <= 0) throw new Exception("获取文件大小失败");
if (filename.Length == 0) throw new