[转]数据下载(二十一)

添加ftp支持,支持服务器登录。

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("ftp://down2.oamo.com:2121/05/31/聚生网管2.10.rar", "www.oamo.com", "www.oamo.com");
            //方法1:使用DownloadData返回的内容。
            FileStream fs = new FileStream("c:\\ee.zip", 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
 {
  string userName;
  string passWord;

  //开始为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, string userName, string passWord)
  {
   url = System.Web.HttpUtility.UrlDecode(url);
   this.userName = userName;
   this.passWord = passWord;
   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);

    if (userName != "" && passWord != "")
    {
     wr.Credentials = new NetworkCredential(userName, passWord);
    }

    //弄一个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,那么意味着,不能用分块的方式下载。
    Uri uri = new Uri(url);
    if (uri.Scheme == Uri.UriSchemeHttp)
    {
     if (length != -1)
     {
      //新玩意:把WebRequest变成HttpWebRequest
      //目的在于向请求头里面添加Content-Range
      HttpWebRequest hwr = (HttpWebRequest)wr;
      hwr.AddRange(di.DownloadChunk.From, di.DownloadChunk.To);
     }
    }
    else
    {
     if (length != -1)
     {
      //新玩意:把WebRequest变成HttpWebRequest
      //目的在于向请求头里面添加Content-Range
      FtpWebRequest hwr = (FtpWebRequest)wr;
      hwr.Method = WebRequestMethods.Ftp.DownloadFile;
      //hwr.AddRange(di.DownloadChunk.From, di.DownloadChunk.To);
     }
    }
    //塞进去
    di.WebRequest = wr;
    //实例化线程
    //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;
  }

  public byte[] DownloadData(string url)
  {
   return DownloadData(url, "", "");
  }

  /// <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, extensionFileName);
     }
     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)
  {
   url = System.Web.HttpUtility.UrlDecode(url);
   WebRequest wr = WebRequest.Create(url);
   if (userName != "" && passWord != "")
   {
    wr.Credentials = new NetworkCredential(userName, passWord);
   }
   WebResponse wsp = wr.GetResponse();
   Uri uri = new Uri(url);
   long length = wsp.ContentLength;
   string s = wsp.ResponseUri.ToString();
   int pointIndex = s.LastIndexOf(".");
   if (pointIndex != -1)
   {
    extensionFileName = s.Substring(pointIndex);
   }
   else
   {
    extensionFileName = "";
   }
   wr.Abort();
   wsp.Close();
   return length;
  }

 }
}

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

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