摘要:最近项目需要做一个类似于迅雷的文件下载功能,这类需求可能比较常见,希望可以帮助到有需要的同学要求:1.支持断点传输2. 多文件同时下载3. 由于是客户内部试用,服务器只支持HTTP文件下载不支持FTP文件(并没有用户权限的要求)由于根据服务器传来的Json字符串解析后进行下载,首先构建Json对象,推荐一个库Newtonsoft.Jsonpublic class Downloa
最近项目需要做一个类似于迅雷的文件下载功能,这类需求可能比较常见,希望可以帮助到有需要的同学
要求:
1.支持断点传输
2. 多文件同时下载
3. 由于是客户内部试用,服务器只支持HTTP文件下载不支持FTP文件(并没有用户权限的要求)
由于根据服务器传来的Json字符串解析后进行下载,首先构建Json对象,推荐一个库Newtonsoft.Json
public class DownloadFile
{
[JsonProperty("police_num")]
public string PoliceNumber { get; set; }
[JsonProperty("equipment_num")]
public string EquipmentNumber { get; set; }
[JsonProperty("upload_date")]
public DateTime UploadDateTime { get; set; }
[JsonProperty("type")]
public string FileType { get; set; }
[JsonProperty("file_name")]
public string FileName { get; set; }
[JsonProperty("file_path")]
public string FilePath { get; set; }
}JsonProperty不区分大小写
public class DownloadTask : ObservableObject
{
/// <summary>
/// </summary>
public static readonly int BufferSize = 1024;
/// <summary>
/// 一次请求大小,每次请求100kb
/// </summary>
public static long Step = 1024*100;
private int _process;
/// <summary>
/// 下载文件参数
/// </summary>
public DownloadFile DownloadFile { get; set; }
/// <summary>
/// 下载文件状态
/// </summary>
public SegmentState SegmentState { get; set; }
/// <summary>
/// 默认存储路径
/// </summary>
public string DefaultDirectory { get; set; }
/// <summary>
/// 当前进度
/// </summary>
public long CurrentSize { get; set; }
/// <summary>
/// 文件流对象,用于生成文件
/// </summary>
public FileStream FileStream { get; set; }
/// <summary>
/// 下载起始位置
/// </summary>
public long RangeFrom { get; set; }
/// <summary>
/// 总大小
/// </summary>
public long TotalSize { get; set; }
/// <summary>
/// 链接地址
/// </summary>
public string Url { get; set; }
/// <summary>
/// 界面显示进度
/// </summary>
public int Process
{
get { return _process; }
set
{
_process = value;
RaisePropertyChanged("Process");
}
}
}下载服务类,DownloaderService.cs,功能包括启动下载,暂停,针对文件是否存在进行初始化,获取文件大小方便界面显示
public class DownloaderService
{
public void OnStart(DownloadTask downloadTask)
{
if (!InitService(downloadTask)) return;
var downloadThread = new Thread(BeginDownloadFile) { IsBackground = true };
downloadThread.Start(downloadTask);
}
public void OnStop(DownloadTask downloadTask)
{
downloadTask.SegmentState = SegmentState.Paused;
}
private bool InitService(DownloadTask downloadTask)
{
try
{
if (string.IsNullOrEmpty(downloadTask.Url))
{
return false;
}
downloadTask.DownloadFile.FileName = downloadTask.Url.Substring(downloadTask.Url.LastIndexOf('/') + 1);
var fullName = Path.Combine(downloadTask.DefaultDirectory, downloadTask.DownloadFile.FileName);
if (File.Exists(fullName))
{
if (downloadTask.TotalSize == 0)
{
GetTotalSize(downloadTask);
}
if (downloadTask.CurrentSize == downloadTask.TotalSize)
{
if (MessageBox.Show("文件已经存在是否覆盖此文件", "TCL", MessageBoxButton.YesNo, MessageBoxImage.Information) !=
MessageBoxResult.Yes)
return false;
File.Delete(fullName);
downloadTask.CurrentSize = 0;
}
downloadTask.FileStream = new FileStream(fullName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
downloadTask.SegmentState = SegmentState.Downloading;
return true;
}
downloadTask.SegmentState = SegmentState.Downloading;
downloadTask.FileStream = new FileStream(fullName, FileMode.OpenOrCreate, FileAccess.ReadWrite);
return true;
}
catch (Exception ex)
{
LogLightConsole.WriteLog(ex.ToString(), EunmMsgType.Error);
return false;
}
}
private void BeginDownloadFile(object obj)
{
try
{
var downloadTask = (DownloadTask)obj;
while (true)
{
if (downloadTask.SegmentState == SegmentState.Paused)
{
downloadTask.FileStream.Close();
break;
}
//从0计数,需要减一
var from = downloadTask.CurrentSize;
if (from < 0)
{
from = 0;
}
var to = downloadTask.CurrentSize + DownloadTask.Step - 1;
if (to >= downloadTask.TotalSize && downloadTask.TotalSize > 0)
{
to = downloadTask.TotalSize - 1;
}
if (downloadTask.TotalSize == 0)
{
GetTotalSize(downloadTask);
}
if (from >= downloadTask.TotalSize || downloadTask.CurrentSize >= downloadTask.TotalSize)
{
downloadTask.SegmentState = SegmentState.Finished;
downloadTask.FileStream.Close();
downloadTask.Process = 100;
return;
}
var request = (HttpWebRequest)WebRequest.Create(downloadTask.Url);
//request.Method = "GET";
request.AddRange("bytes", from, to);
request.Timeout = 1000 * 20;
var response = (HttpWebResponse)request.GetResponse();
var buffer = new byte[1024];
using (var stream = response.GetResponseStream())
{
if (stream != null)
{
var size = stream.Read(buffer, 0, buffer.Length);
while (size > 0)
{
//只将读出的字节写入文件
downloadTask.FileStream.Write(buffer, 0, size);
downloadTask.CurrentSize += size;
size = stream.Read(buffer, 0, buffer.Length);
downloadTask.Process = (int)(downloadTask.CurrentSize * 100 / downloadTask.TotalSize);
downloadTask.FileStream.Flush();
}
}
//如果返回的response头中Content-Range值为空,说明服务器不支持Range属性,不支持断点续传,返回的是所有数据
if (response.Headers["Content-Range"] == null)
{
downloadTask.SegmentState = SegmentState.Finished;
}
}
}
}
catch (Exception ex)
{
LogLightConsole.WriteLog("BeginDownloadFile" + ex, EunmMsgType.Error);
}
}
/// <summary>
/// 获取文件总大小
/// </summary>
public void GetTotalSize(DownloadTask downloadTask)
{
var request = (HttpWebRequest)WebRequest.Create(downloadTask.Url);
//request.Method = "POST";
//request.Headers.Add("charset:" + "utf-8");
request.Timeout = 1000 * 20;
var response = (HttpWebResponse)request.GetResponse();
downloadTask.TotalSize = response.ContentLength;
}
}HTTP 1.1版本本身已经扩展了断点传输功能,但是这需要服务器同时也支持,这只是提供了一个简单的例子,具体的针对数据上下文以及成功失败的callback函数,就是做一下委托封装用就好,谢谢大家支持!
response.Headers["Content-Range"] == null 说明服务器不支持断点传输