[转]数据下载(十九)

让下载类纯粹一些,在下载类的内部,不再保存为文件,如果要保存,那是用户自己的事情,提供了两种形式返回下载的内容:方法的返回值和事件参数。

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Threading;

namespace prjDownLoad
{
    class Program
    {
        static void Main(string[] args)
        {
            Thread t = new Thread(new ThreadStart(Down));
            t.Start();
        }

        static void Down()
        {
           
            DownloadUtil du = new DownloadUtil();
            //订阅事件
            du.DownloadChunkCompleted += new DownloadChunkCompletedHandler(du_DownloadChunkCompleted);
            du.DownloadProgress += new DownloadProgressHandler(du_DownloadProgress);
            du.DownloadCompleted += new DownloadCompletedHandler(du_DownloadCompleted);
            //多线程
            byte[] bs = du.DownloadData("http://down.360safe.com/setup.exe");
            ////单线程
            //byte[] bs = du.DownloadData("http://blog.sina.com.cn/dalishuishou");
            //方法1:使用DownloadData返回的内容。
            FileStream fs = new FileStream("c:\\ee.pptx", FileMode.Create, FileAccess.Write);
            fs.Write(bs, 0, bs.Length);
            fs.Flush();
            fs.Close();
        }

        /// <summary>
        /// 下载完成事件
        /// </summary>
        static void du_DownloadCompleted(byte[] bs, string extensionFileName)
        {
            //方法2:在下载完成事件中处理。
            FileStream fs = new FileStream("c:\\xx.pptx", FileMode.Create, FileAccess.Write);
            fs.Write(bs, 0, bs.Length);
            fs.Flush();
            fs.Close();
            Console.WriteLine("全部下载完成。");
        }

        /// <summary>
        /// 下载过程事件处理过程
        /// </summary>
        /// <param name="bs"></param>
        static void du_DownloadProgress(DownloadChunk dc)
        {
            Console.WriteLine("线程{0}下载了{1}字节",dc.Number,dc.Content.Length);
        }

        /// <summary>
        /// 区块下载完成事件处理过程
        /// </summary>
        static void du_DownloadChunkCompleted(DownloadChunk dc)
        {
            Console.WriteLine("第{0}块下载完成,数据量:{1}",dc.Number,dc.Content.Length);
        }
    }
}

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;

namespace prjDownLoad
{
    /// <summary>
    /// 区块下载完成委托
    /// </summary>
    /// <param name="dc">下载的区块</param>
    public delegate void DownloadChunkCompletedHandler(DownloadChunk dc);
}
using System;
using System.Collections.Generic;
using System.Text;

namespace prjDownLoad
{
    /// <summary>
    /// 下载全部完成委托
    /// </summary>
    /// <param name="bs">完成的byte数组</param>
    /// <param name="extensionFileName">后缀名</param>
    public delegate void DownloadCompletedHandler(byte[] bs,string extensionFileName);
   
}
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;

namespace prjDownLoad
{
    /// <summary>
    /// 下载过程委托
    /// </summary>
    /// <param name="dc">下载的区块</param>
    public delegate void DownloadProgressHandler(DownloadChunk dc);
}
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];
        DownloadInfo[] dis=new DownloadInfo[ThreadSum];
        //已经完成的线程个数
        int threadCount;
        //多线程下载时的容器
        byte[] allChunkContent;

        //信号机
        AutoResetEvent autoResetEvent=new AutoResetEvent(false);
        /// <summary>
        /// 区块下载完成事件
        /// </summary>
        public event DownloadChunkCompletedHandler DownloadChunkCompleted;
        /// <summary>
        /// 下载过程事件
        /// </summary>
        public event DownloadProgressHandler DownloadProgress;
        /// <summary>
        /// 某个任务完全下载完成。
        /// </summary>
        public event DownloadCompletedHandler DownloadCompleted;
        //临时保存字节数组的集合(单线程下载时使用)
        List<byte> bytes = new List<byte>();

        //扩展名
        string extensionFileName;
        /// <summary>
        /// 下载数据
        /// </summary>
        /// <param name="url">要下载的url</param>
        /// <param name="fileName">要保存的文件名</param>
        /// <returns>下载完成的byte数组</returns>
        public byte[] DownloadData(string url)
        {
            long length=GetLength(url);
            if (length==-1)
            {
                WebRequest wr = WebRequest.Create(url);

                wr.BeginGetResponse(new AsyncCallback(GetResponseCallBack), wr);
                autoResetEvent.WaitOne();
                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
                    dis[i]=new DownloadInfo();
                    DownloadInfo di = dis[i];
                    //再弄一个DownloadChunk
                    DownloadChunk dc = new DownloadChunk();
                    di.DownloadChunk = dc;
                    //每块的起始位置
                    di.DownloadChunk.From = i * blockSize;
                    //每块的结束位置(最后一块要加上剩下的大小)
                    di.DownloadChunk.To = (int)(i == ThreadSum - 1 ? (i + 1) * blockSize + surPlus : (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));
                    //也可以考虑将执行的内容加入线程池中。
                    ThreadPool.QueueUserWorkItem(new WaitCallback(DownloadByChunk), di);
                }
                ////如果上面没加入线程池,那么在这里启动线程
                //for (int i = 0; i < ThreadSum; i++)
                //{
                //    ts[i].Start(dis[i]);
                //}
                //还是等着呗
                autoResetEvent.WaitOne();
                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="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(di.DownloadChunk);
                }
                di.ReadStream.BeginRead(di.Buffer, 0, di.Buffer.Length, new AsyncCallback(ReadCallBackByChunk), di);
            }
            else
            {
                di.ReadStream.Close();
                if (DownloadChunkCompleted != null)
                {
                    DownloadChunkCompleted(di.DownloadChunk);
                }
                //某个块中的数据全部回来了,再加入容器中
                Array.Copy(di.DownloadChunk.Content, 0, allChunkContent, di.DownloadChunk.From, di.DownloadChunk.Content.Length);
                //线程计数加一
                threadCount++;
                //如果所有的线程都完了,才发信号,一个线程完了,不发信号
                if (threadCount==ThreadSum)
                {
                    if (DownloadCompleted!=null)
                    {
                        DownloadCompleted(allChunkContent,"");
                    }
                    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(di.DownloadChunk);
                }
                di.ReadStream.BeginRead(di.Buffer, 0, di.Buffer.Length, new AsyncCallback(ReadCallBack), di);
            }
            else
            {
                di.ReadStream.Close();
                if (DownloadChunkCompleted != null)
                {
                    DownloadChunkCompleted(di.DownloadChunk);
                }
                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;
        }
    }
}

转摘自:http://blog.sina.com.cn/s/blog_49458c270100ipeu.html

posted @ 2011-03-02 00:06  愤怒的熊猫  阅读(152)  评论(0编辑  收藏  举报