[转]数据下载(二十)

整合了单线程和多线程。

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.txt", 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"+extensionFileName, 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
    {
        //开始为1个线程
        int ThreadSum = 1;
        //线程数组
        Thread[] ts;
        DownloadInfo[] dis;
        //已经完成的线程个数
        int completedThreadCount=0;
        //多线程下载时的容器
        byte[] allChunkContent=new byte[0];

        //信号机
        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;
        //数据长度
        long length;
        /// <summary>
        /// 下载数据
        /// </summary>
        /// <param name="url">要下载的url</param>
        /// <param name="fileName">要保存的文件名</param>
        /// <returns>下载完成的byte数组</returns>
        public byte[] DownloadData(string url)
        {
            length = GetLength(url);
            //每块的大小
            int blockSize =0;
            //剩下的大小
            int surPlus=0;

            if (length!=-1)
            {
                ThreadSum = 5;
                //弄个大小相当的容器
                allChunkContent = new byte[length];
                //每块的大小
                blockSize = (int)(length / ThreadSum);
                //剩下的大小
                surPlus = (int)(length % ThreadSum);
            }
           
            ts=new Thread[ThreadSum];
            dis=new DownloadInfo[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)
                //如果length为-1,那么意味着,不能用分块的方式下载。
                if (length!=-1)
                {
                    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 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);
                }
                //某个块中的数据全部回来了,再加入容器中
                if (length!=-1)
                {
                    //如果是多线程
                    Array.Copy(di.DownloadChunk.Content, 0, allChunkContent, di.DownloadChunk.From, di.DownloadChunk.Content.Length);
                }
                else
                {
                    //如果是单线程。
                    allChunkContent = di.DownloadChunk.Content;
                }
               
                //完成的线程计数加一
                completedThreadCount++;
                //如果所有的线程都完了,才发信号,一个线程完了,不发信号

                if (completedThreadCount == ThreadSum)
                {
                    if (DownloadCompleted != null)
                    {
                        DownloadCompleted(allChunkContent, "");
                    }
                    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();
            HttpWebResponse hwr = (HttpWebResponse)wsp;
            long length = wsp.ContentLength;
            string s = hwr.ResponseUri.ToString();
            int pointIndex = s.LastIndexOf(".");
            if (pointIndex!=-1)
            {
                extensionFileName = s.Substring(pointIndex);
            }
            else
            {
                extensionFileName = "";
            }
            wr.Abort();
            wsp.Close();
            return length;
        }
    }
}

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