多线程,分块下载。
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Threading;
namespace prjDownLoad
{
class Program
{
//准备了一个用来保存的文件流。
static FileStream fs;
static void Main(string[] args)
{
Thread t = new Thread(new ThreadStart(Down));
t.Start();
}
static void Down()
{
fs = new FileStream("c:\\ee.pptx", FileMode.Create, FileAccess.Write);
DownloadUtil du = new DownloadUtil();
//订阅事件
du.DownloadCompleted += new DownloadCompletedHandler(du_DownloadCompleted);
du.DownloadProgress += new DownloadProgressHandler(du_DownloadProgress);
//多线程
byte[] bs = du.DownloadData("http://mschnlnine.vo.llnwd.net/d1/pdc08/PPTX/BB01.pptx");
单线程
//byte[] bs = du.DownloadData("http://blog.sina.com.cn/dalishuishou");
Console.WriteLine(bs.Length);
fs.Write(bs, 0, bs.Length);
fs.Flush();
fs.Close();
}
/// <summary>
/// 下载过程事件处理过程
/// </summary>
/// <param name="bs"></param>
static void du_DownloadProgress(byte[] bs)
{
Console.WriteLine("下载了{0}字节",bs.Length);
}
/// <summary>
/// 下载完成事件处理过程
/// </summary>
static void du_DownloadCompleted()
{
Console.WriteLine("下载完成了。");
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
namespace prjDownLoad
{
//下载完成委托
public delegate void DownloadCompletedHandler();
}
using System;
using System.Collections.Generic;
using System.Text;
namespace prjDownLoad
{
//下载过程委托
public delegate void DownloadProgressHandler(byte[] bs);
}
using System;
using System.Collections.Generic;
using System.Text;
namespace prjDownLoad
{
/// <summary>
/// 下载的数据块
/// </summary>
[Serializable]
public class DownloadChunk
{
//临时存储的集合
List<byte> content = new List<byte>();
/// <summary>
/// 整个块的内容
/// </summary>
public byte[] Content
{
get { return content.ToArray(); }
}
/// <summary>
/// 向块中添加内容
/// </summary>
/// <param name="bs"></param>
public void AddContent(byte[] bs)
{
content.AddRange(bs);
}
int from;
/// <summary>
/// 开始位置
/// </summary>
public int From
{
get { return from; }
set { from = value; }
}
int to;
/// <summary>
/// 结束位置
/// </summary>
public int To
{
get { return to; }
set { to = value; }
}
int number;
/// <summary>
/// 块编号
/// </summary>
public int Number
{
get { return number; }
set { number = value; }
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
namespace prjDownLoad
{
/// <summary>
/// 下载信息实体类
/// </summary>
[Serializable]
public class DownloadInfo
{
DownloadChunk downloadChunk;
public DownloadChunk DownloadChunk
{
get { return downloadChunk; }
set { downloadChunk = value; }
}
WebRequest webRequest;
/// <summary>
/// 这个WebRequest中如果是分块下载
/// 那么在Range中要加上开始和结束位置
/// </summary>
public WebRequest WebRequest
{
get { return webRequest; }
set { webRequest = value; }
}
Stream readStream;
/// <summary>
/// 读取数据的流
/// </summary>
public Stream ReadStream
{
get { return readStream; }
set { readStream = value; }
}
byte[] buffer = new byte[1024];
/// <summary>
/// 缓冲字节数组,用来存储从流中读取的数据的容器
/// </summary>
public byte[] Buffer
{
get { return buffer; }
set { buffer = value; }
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Net;
using System.IO;
namespace prjDownLoad
{
public class DownloadUtil
{
//固定为5个线程
const int ThreadSum = 5;
//线程数组
Thread[] ts=new Thread[ThreadSum];
//已经完成的线程个数
int threadCount;
//多线程下载时的容器
byte[] allChunkContent;
//信号机
AutoResetEvent autoResetEvent=new AutoResetEvent(false);
/// <summary>
/// 下载完成事件
/// </summary>
public event DownloadCompletedHandler DownloadCompleted;
/// <summary>
/// 下载过程事件
/// </summary>
public event DownloadProgressHandler DownloadProgress;
//临时保存字节数组的集合(单线程下载时使用)
List<byte> bytes = new List<byte>();
//要存储的fileName
string fileName;
/// <summary>
/// 下载数据
/// </summary>
/// <param name="url">要下载的url</param>
/// <param name="fileName">要保存的文件名</param>
/// <returns>下载完成的byte数组</returns>
public byte[] DownloadData(string url,string fileName)
{
this.fileName = fileName;
long length=GetLength(url);
if (length==-1)
{
WebRequest wr = WebRequest.Create(url);
wr.BeginGetResponse(new AsyncCallback(GetResponseCallBack), wr);
autoResetEvent.WaitOne();
if (fileName != "")
{
FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
fs.Write(bytes.ToArray(), 0, bytes.ToArray().Length);
fs.Flush();
fs.Close();
}
return bytes.ToArray();
}
else
{
//弄个大小相当的容器
allChunkContent=new byte[length];
//每块的大小
int blockSize =(int) (length / ThreadSum);
//剩下的大小
int surPlus=(int)(length%ThreadSum);
//开上几个线程,一块弄
for (int i = 0; i < ThreadSum; i++)
{
//老套路
WebRequest wr = WebRequest.Create(url);
//新玩意:把WebRequest变成HttpWebRequest
//目的在于向请求头里面添加Content-Range
HttpWebRequest hwr = (HttpWebRequest)wr;
//弄一个DownloadInfo
DownloadInfo di = new DownloadInfo();
//再弄一个DownloadChunk
DownloadChunk dc = new DownloadChunk();
di.DownloadChunk = dc;
//每块的起始位置
di.DownloadChunk.From = i * blockSize;
//每块的结束位置(最后一块要加上剩下的大小)
di.DownloadChunk.To = (int)(i == ThreadSum - 1 ? (i + 1) * blockSize +yu: (i + 1) * blockSize);
//给每个块编号
di.DownloadChunk.Number = i + 1;
//向请求中添加获取的范围(Content-Range)
hwr.AddRange(di.DownloadChunk.From, di.DownloadChunk.To);
//塞进去
di.WebRequest = hwr;
//实例化线程
ts[i] = new Thread(new ParameterizedThreadStart(DownloadByChunk));
//开跑
ts[i].Start(di);
}
//还是等着呗
autoResetEvent.WaitOne();
if (fileName!="")
{
FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write);
fs.Write(allChunkContent, 0, allChunkContent.Length);
fs.Flush();
fs.Close();
}
return allChunkContent;
}
}
/// <summary>
/// 就是把单线程处理部分的内容抄了一个过来。
/// </summary>
/// <param name="obj"></param>
private void DownloadByChunk(object obj)
{
DownloadInfo di = obj as DownloadInfo;
di.WebRequest.BeginGetResponse(new AsyncCallback(GetResponseCallBackByChunk), di);
}
/// <summary>
/// 下载数据
/// </summary>
/// <param name="url">要下载的url</param>
/// <returns>下载完成的byte数组</returns>
public byte[] DownloadData(string url)
{
return DownloadData(url, "");
}
/// <summary>
/// 当有回应对象时调用这个方法来处理(多线程)。
/// </summary>
/// <param name="result"></param>
void GetResponseCallBackByChunk(IAsyncResult result)
{
DownloadInfo di = result.AsyncState as DownloadInfo;
WebRequest wr = di.WebRequest;
WebResponse wsp = wr.EndGetResponse(result);
Stream st = wsp.GetResponseStream();
di.ReadStream = st;
st.BeginRead(di.Buffer, 0, di.Buffer.Length, new AsyncCallback(ReadCallBackByChunk), di);
}
/// <summary>
/// 当有回应对象时调用这个方法来处理(单线程)。
/// </summary>
/// <param name="result"></param>
void GetResponseCallBack(IAsyncResult result)
{
WebRequest wr = result.AsyncState as WebRequest;
WebResponse wsp = wr.EndGetResponse(result);
Stream st = wsp.GetResponseStream();
DownloadInfo di = new DownloadInfo();
di.ReadStream = st;
st.BeginRead(di.Buffer, 0, di.Buffer.Length, new AsyncCallback(ReadCallBack), di);
}
/// <summary>
/// 一次读取完成后调用的方法(多线程)
/// </summary>
/// <param name="result"></param>
void ReadCallBackByChunk(IAsyncResult result)
{
DownloadInfo di = result.AsyncState as DownloadInfo;
int x = di.ReadStream.EndRead(result);
if (x > 0)
{
byte[] bs = SubBytes(di.Buffer, x);
//回来的数据先放到块中
di.DownloadChunk.AddContent(bs);
if (DownloadProgress != null)
{
DownloadProgress(bs);
}
di.ReadStream.BeginRead(di.Buffer, 0, di.Buffer.Length, new AsyncCallback(ReadCallBackByChunk), di);
}
else
{
di.ReadStream.Close();
if (DownloadCompleted != null)
{
DownloadCompleted();
}
//某个块中的数据全部回来了,再加入容器中
Array.Copy(di.DownloadChunk.Content, 0, allChunkContent, di.DownloadChunk.From, di.DownloadChunk.Content.Length);
//线程计数加一
threadCount++;
//如果所有的线程都完了,才发信号,一个线程完了,不发信号
if (threadCount==ThreadSum)
{
fileName = "";
autoResetEvent.Set();
}
}
}
/// <summary>
/// 一次读取完成后调用的方法(单线程)
/// </summary>
/// <param name="result"></param>
void ReadCallBack(IAsyncResult result)
{
DownloadInfo di = result.AsyncState as DownloadInfo;
int x = di.ReadStream.EndRead(result);
if (x > 0)
{
byte[] bs=SubBytes(di.Buffer, x);
bytes.AddRange(bs);
if (DownloadProgress != null)
{
DownloadProgress(bs);
}
di.ReadStream.BeginRead(di.Buffer, 0, di.Buffer.Length, new AsyncCallback(ReadCallBack), di);
}
else
{
di.ReadStream.Close();
if (DownloadCompleted != null)
{
DownloadCompleted();
}
fileName = "";
autoResetEvent.Set();
}
}
/// <summary>
/// 截取byte数组中的内容
/// </summary>
/// <param name="bs">源数组</param>
/// <param name="length">要截取的长度</param>
/// <returns>截取的结果数组</returns>
byte[] SubBytes(byte[] bs, int length)
{
byte[] temp=new byte[length];
Array.Copy(bs, 0, temp, 0, length);
return temp;
}
/// <summary>
/// 获取要下载的数据的长度
/// 如果为-1,那么不能分块下载
/// </summary>
/// <param name="url">要下载的Url</param>
/// <returns>内容的长度</returns>
long GetLength(string url)
{
WebRequest wr = WebRequest.Create(url);
WebResponse wsp = wr.GetResponse();
long length = wsp.ContentLength;
wr.Abort();
wsp.Close();
return length;
}
}
}