Asp.net 2.0 文件下载[支持多线程, 断点续传功能](示例代码下载)

(一) . 概述

        最近做了个C/S文件下载工具, 支持多任务, 多线程和断点续传功能. 其中部分代码是从网上找来的, 自己改了

许多Thread Bug, 并增加多任务, 断点续传等功能.  

        由于公司具有代码所有权, 不能将源代码共享.  自己对比较Asp.net感兴趣, 业余时间自己做了个简单的, 基于

Asp.net 2.0的, 目前能够执行对一个文件的下载任务, 但已经实现了多线程, 断点续传功能.  根据需要您可以增加

多任务 功能, 分享一下, 互相学习!  互相借鉴!

       时间仓促, 此程序还没有做很多参数方面的优化. 可以作参考用.

              

(二).运行效果

 (三). 代码

  1. 核心 DownLoadState.cs 文件代码

   1  ///   <summary>
   2  ///  Author: [ ChengKing(ZhengJian) ] 
   3  ///  Blog:   Http://blog.youkuaiyun.com/ChengKing
   4  ///  注:从网上找了个优秀代码
   5  ///  扩展如下功能: 
   6  ///    1. 解决一些线程相关的Bug; 
   7  ///    2.扩展用控制文件实现断点续传功能.
   8  ///   </summary>
   9  namespace  DownLoadComponent
  10  {
  11       ///   <summary>
  12       ///  多线程辅助类(Add by ChengKing)
  13       ///   </summary>
  14       public   class  Task
  15      {
  16           string  _FromFileName;
  17           string  _ToFileName;
  18           int  _ThreadNum;
  19 
  20           public  Task( string  FromFileName,  string  ToFileName,  int  ThreadNum)
  21          {
  22               this ._FromFileName  =  FromFileName;
  23               this ._ToFileName  =  ToFileName;
  24               this ._ThreadNum  =  ThreadNum;
  25          }
  26 
  27           public   string  FromFileName
  28          {
  29               get
  30              {
  31                   return  _FromFileName;
  32              }
  33               set
  34              {
  35                  _FromFileName  =  value;
  36              }
  37          }
  38           public   string  ToFileName
  39          {
  40               get
  41              {
  42                   return  _ToFileName;
  43              }
  44               set
  45              {
  46                  _ToFileName  =  value;
  47              }
  48          }
  49           public   int  ThreadNum
  50          {
  51               get
  52              {
  53                   return  _ThreadNum;
  54              }
  55               set
  56              {
  57                  _ThreadNum  =  value;
  58              }
  59          }
  60      }
  61 
  62       ///   <summary>
  63       ///  记录下载的字节位置
  64       ///   </summary>
  65       public   class  DownLoadState
  66      {
  67           private   string  _FileName;
  68 
  69           private   string  _AttachmentName;
  70           private   int  _Position;
  71           private   string  _RequestURL;
  72           private   string  _ResponseURL;
  73           private   int  _Length;
  74 
  75           private   byte [] _Data;
  76 
  77           public   string  FileName
  78          {
  79               get
  80              {
  81                   return  _FileName;
  82              }
  83          }
  84 
  85           public   int  Position
  86          {
  87               get
  88              {
  89                   return  _Position;
  90              }
  91          }
  92 
  93           public   int  Length
  94          {
  95               get
  96              {
  97                   return  _Length;
  98              }
  99          }
 100 
 101 
 102           public   string  AttachmentName
 103          {
 104               get
 105              {
 106                   return  _AttachmentName;
 107              }
 108          }
 109 
 110           public   string  RequestURL
 111          {
 112               get
 113              {
 114                   return  _RequestURL;
 115              }
 116          }
 117 
 118           public   string  ResponseURL
 119          {
 120               get
 121              {
 122                   return  _ResponseURL;
 123              }
 124          }
 125 
 126 
 127           public   byte [] Data
 128          {
 129               get
 130              {
 131                   return  _Data;
 132              }
 133          }
 134 
 135           internal  DownLoadState( string  RequestURL,  string  ResponseURL,  string  FileName,  string  AttachmentName,  int  Position,  int  Length,  byte [] Data)
 136          {
 137               this ._FileName  =  FileName;
 138               this ._RequestURL  =  RequestURL;
 139               this ._ResponseURL  =  ResponseURL;
 140               this ._AttachmentName  =  AttachmentName;
 141               this ._Position  =  Position;
 142               this ._Data  =  Data;
 143               this ._Length  =  Length;
 144          }
 145 
 146           internal  DownLoadState( string  RequestURL,  string  ResponseURL,  string  FileName,  string  AttachmentName,  int  Position,  int  Length, ThreadCallbackHandler tch)
 147          {
 148               this ._RequestURL  =  RequestURL;
 149               this ._ResponseURL  =  ResponseURL;
 150               this ._FileName  =  FileName;
 151               this ._AttachmentName  =  AttachmentName;
 152               this ._Position  =  Position;
 153               this ._Length  =  Length;
 154               this ._ThreadCallback  =  tch;
 155          }
 156 
 157           internal  DownLoadState( string  RequestURL,  string  ResponseURL,  string  FileName,  string  AttachmentName,  int  Position,  int  Length)
 158          {
 159               this ._RequestURL  =  RequestURL;
 160               this ._ResponseURL  =  ResponseURL;
 161               this ._FileName  =  FileName;
 162               this ._AttachmentName  =  AttachmentName;
 163               this ._Position  =  Position;
 164               this ._Length  =  Length;
 165          }
 166 
 167           private  ThreadCallbackHandler _ThreadCallback;
 168 
 169           //
 170           internal   void  StartDownloadFileChunk()
 171          {
 172               if  ( this ._ThreadCallback  !=   null )
 173              {
 174                   this ._ThreadCallback( this ._RequestURL,  this ._FileName,  this ._Position,  this ._Length);
 175              }
 176          }
 177 
 178      }
 179 
 180       // 委托代理线程的所执行的方法签名一致
 181       public   delegate   void  ThreadCallbackHandler( string  S,  string  s,  int  I,  int  i);
 182 
 183       // 异常处理动作
 184       public   enum  ExceptionActions
 185      {
 186          Throw,
 187          CancelAll,
 188          Ignore,
 189          Retry
 190      }
 191 
 192       ///   <summary>
 193       ///  包含 Exception 事件数据的类
 194       ///   </summary>
 195       public   class  ExceptionEventArgs : System.EventArgs
 196      {
 197           private  System.Exception _Exception;
 198           private  ExceptionActions _ExceptionAction;
 199 
 200           private  DownLoadState _DownloadState;
 201 
 202           public  DownLoadState DownloadState
 203          {
 204               get
 205              {
 206                   return  _DownloadState;
 207              }
 208          }
 209 
 210           public  Exception Exception
 211          {
 212               get
 213              {
 214                   return  _Exception;
 215              }
 216          }
 217 
 218           public  ExceptionActions ExceptionAction
 219          {
 220               get
 221              {
 222                   return  _ExceptionAction;
 223              }
 224               set
 225              {
 226                  _ExceptionAction  =  value;
 227              }
 228          }
 229 
 230           internal  ExceptionEventArgs(System.Exception e, DownLoadState DownloadState)
 231          {
 232               this ._Exception  =  e;
 233               this ._DownloadState  =  DownloadState;
 234          }
 235      }
 236 
 237       ///   <summary>
 238       ///  包含 DownLoad 事件数据的类
 239       ///   </summary>
 240       public   class  DownLoadEventArgs : System.EventArgs
 241      {
 242           private  DownLoadState _DownloadState;
 243 
 244           public  DownLoadState DownloadState
 245          {
 246               get
 247              {
 248                   return  _DownloadState;
 249              }
 250          }
 251 
 252           public  DownLoadEventArgs(DownLoadState DownloadState)
 253          {
 254               this ._DownloadState  =  DownloadState;
 255          }
 256 
 257      }
 258 
 259       ///   <summary>
 260       ///  支持断点续传多线程下载的类
 261       ///   </summary>
 262       public   class  HttpWebClient
 263      {
 264           private   static   object  _SyncLockObject  =   new   object ();
 265 
 266           public   delegate   void  DataReceiveEventHandler(HttpWebClient Sender, DownLoadEventArgs e);
 267 
 268           public   event  DataReceiveEventHandler DataReceive;  // 接收字节数据事件
 269 
 270           public   delegate   void  ExceptionEventHandler(HttpWebClient Sender, ExceptionEventArgs e);
 271 
 272           public   event  ExceptionEventHandler ExceptionOccurrs;  // 发生异常事件
 273 
 274           private   int  _FileLength;  // 下载文件的总大小
 275 
 276           public   static  ArrayList threads;
 277 
 278           public   int  FileLength
 279          {
 280               get
 281              {
 282                   return  _FileLength;
 283              }
 284          }
 285 
 286           ///   <summary>
 287           ///  分块下载文件
 288           ///   </summary>
 289           ///   <param name="Address"> URL 地址 </param>
 290           ///   <param name="FileName"> 保存到本地的路径文件名 </param>
 291           ///   <param name="ChunksCount"> 块数,线程数 </param>
 292           public   void  DownloadFile( string  Address,  string  FileName,  int  ChunksCount)
 293          {
 294               int  p  =   0 //  position
 295               int  s  =   0 //  chunk size
 296               string  a  =   null ;
 297              HttpWebRequest hwrq;
 298              HttpWebResponse hwrp  =   null ;
 299               try
 300              {
 301 
 302                  hwrq  =  (HttpWebRequest)WebRequest.Create( this .GetUri(Address));
 303                   // hwrq.Timeout = 20000000;
 304                   // if (hwrq.HaveResponse == false)
 305                   //     return;
 306                   // hwrq.ProtocolVersion =HttpVersion.Version10;
 307                   // WebProxy wp = WebProxy.GetDefaultProxy();
 308                   // hwrq.Proxy = wp;
 309                  hwrq.Method  =   " GET " ;
 310                   try
 311                  {
 312                      hwrp  =  (HttpWebResponse)hwrq.GetResponse();
 313                  }
 314                   catch  (Exception e)
 315                  {
 316                       throw   new  Exception(e.Message);
 317                  }
 318 
 319                   long  L  =  hwrp.ContentLength;
 320 
 321                   // 如果文件太小, 就不用分多线程, 用一个线程下载即可. (目前控制在800K) 
 322                   // if (L < 800000)
 323                   // {
 324                   //     ChunksCount = 1;
 325                   // }
 326 
 327                  hwrq.Credentials  =   this .m_credentials;
 328 
 329                  L  =  ((L  ==   - 1 ||  (L  >   0x7fffffff ))  ?  (( long ) 0x7fffffff ) : L;  // Int32.MaxValue 该常数的值为 2,147,483,647; 即十六进制的 0x7FFFFFFF
 330 
 331                   int  l  =  ( int )L;
 332 
 333                   this ._FileLength  =  l;
 334 
 335                   bool  b  =  (hwrp.Headers[ " Accept-Ranges " !=   null   &  hwrp.Headers[ " Accept-Ranges " ==   " bytes " );
 336                  a  =  hwrp.Headers[ " Content-Disposition " ];  // attachment
 337                   if  (a  !=   null )
 338                  {
 339                      a  =  a.Substring(a.LastIndexOf( " filename= " +   9 );
 340                  }
 341                   else
 342                  {
 343                      a  =  FileName;
 344                  }
 345 
 346                   int  ss  =  s;
 347                   if  (b)
 348                  {
 349                       if  (ExistControlFile(FileName))  // 是否存在文件
 350                      {
 351                           string [] strBlocks  =   this .ReadInfFromControlFile(FileName).Split( new   char [ 2 ] {  ' \r ' ' \n '  });
 352                           for  ( int  i  =   0 ; i  <  strBlocks.Length; i ++ )
 353                          {
 354                               if  (strBlocks[i].Trim().Length  !=   0   &&  strBlocks[i].Substring(strBlocks[i].Length  -   1 ==   " 0 " )
 355                              {
 356                                   string [] strRecord  =  strBlocks[i].Split( ' , ' );
 357                                   int  p2  =   int .Parse(strRecord[ 0 ]);
 358                                   int  s2  =   int .Parse(strRecord[ 1 ]);
 359                                  DownLoadState x  =   new  DownLoadState(Address, hwrp.ResponseUri.AbsolutePath, FileName, a, p2, s2,  new  ThreadCallbackHandler( this .DownloadFileChunk));
 360                                  Thread t  =   new  Thread( new  ThreadStart(x.StartDownloadFileChunk));
 361                                   if  (threads  ==   null )
 362                                  {
 363                                      threads  =   new  ArrayList();
 364                                  }
 365                                  threads.Add(t);
 366                                  t.Start();
 367                              }
 368 
 369 
 370                          }
 371 
 372                      }
 373                       else
 374                      {
 375                           // 建立控制文件
 376                          FileStream fs  =  File.Create( this .GetControlFileName(FileName));
 377                          fs.Close();
 378 
 379                           if  (File.Exists(FileName))
 380                          {
 381                              FileInfo fi  =   new  FileInfo(FileName);
 382                               if  (fi.Length  ==  L)
 383                              {
 384                                   this .AddendInfToControlFile(FileName,  0 0 );
 385                                   this .UpdateControlFile(FileName,  0 0 );
 386                                   return ;
 387                              }
 388                          }
 389 
 390                          s  =  l  /  ChunksCount;
 391                           if  (s  <   2   *   64   *   1024 // 块大小至少为 128 K 字节
 392                          {
 393                              s  =   2   *   64   *   1024 ;
 394                          }
 395                          ss  =  s;
 396                           int  i  =   0 ;
 397                           while  (l  >=  s)
 398                          {
 399                              l  -=  s;
 400                               if  (l  <  s)
 401                              {
 402                                  s  +=  l;
 403                              }
 404                               if  (i ++   >   0 )
 405                              {
 406                                  DownLoadState x  =   new  DownLoadState(Address, hwrp.ResponseUri.AbsolutePath, FileName, a, p, s,  new  ThreadCallbackHandler( this .DownloadFileChunk));
 407 
 408                                  AddendInfToControlFile(FileName, p, s);
 409                                  Thread t  =   new  Thread( new  ThreadStart(x.StartDownloadFileChunk));
 410                                   if  (threads  ==   null )
 411                                  {
 412                                      threads  =   new  ArrayList();
 413                                  }
 414                                  threads.Add(t);
 415                                  t.Start();
 416 
 417                              }
 418                              p  +=  s;
 419                          }
 420                          s  =  ss;
 421 
 422                          AddendInfToControlFile(FileName,  0 , s);
 423                          DownLoadState x1  =   new  DownLoadState(Address, hwrp.ResponseUri.AbsolutePath, FileName, a,  0 , s,  new  ThreadCallbackHandler( this .DownloadFileChunk));
 424                          Thread t2  =   new  Thread( new  ThreadStart(x1.StartDownloadFileChunk));
 425                           if  (threads  ==   null )
 426                          {
 427                              threads  =   new  ArrayList();
 428                          }
 429                          threads.Add(t2);
 430                          t2.Start();
 431                      }
 432                  }
 433                   // 如果服务器不支持断点续传(Accept-Range), 则使用单线程下载
 434                   else
 435                  {
 436                      AddendInfToControlFile(FileName,  0 , l);
 437                      DownLoadState x  =   new  DownLoadState(Address, hwrp.ResponseUri.AbsolutePath, FileName, a,  0 , l,  new  ThreadCallbackHandler( this .DownloadFileChunk));
 438                      Thread t  =   new  Thread( new  ThreadStart(x.StartDownloadFileChunk));
 439                       if  (threads  ==   null )
 440                      {
 441                          threads  =   new  ArrayList();
 442                      }
 443                      threads.Add(t);
 444                      t.Start();
 445                  }
 446              }
 447               catch  (Exception e)
 448              {
 449                   // if (blnReturn == true)
 450                   // {
 451                   //     return;
 452                   // }
 453 
 454                  ExceptionActions ea  =  ExceptionActions.Throw;
 455                   if  (ea  ==  ExceptionActions.Throw)
 456                  {
 457                       if  ( ! (e  is  WebException)  &&   ! (e  is  SecurityException))
 458                      {
 459                           throw   new  WebException( " net_webclient " , e);
 460                      }
 461                       throw ;
 462                  }
 463 
 464 
 465                   // if (this.ExceptionOccurrs != null)
 466                   // {                    
 467                   //     DownLoadState x = new DownLoadState(Address, hwrp.ResponseUri.AbsolutePath, FileName, a, p, s);
 468 
 469                   //     ExceptionEventArgs eea = new ExceptionEventArgs(e, x);
 470                   //     ExceptionOccurrs(this, eea);
 471                   //     ea = eea.ExceptionAction;
 472                   // }
 473 
 474              }
 475 
 476          }

477 
 478         #region 操作控制文件(By King Zheng)
 479 
 480         /// <summary>
 481         /// 插入文件块信息到控制文件(Add by ChengKing)
 482         /// </summary>
 483         /// <param name="FileName"></param>
 484         /// <param name="Position"></param>
 485         /// <param name="Length"></param>
 486         private void AddendInfToControlFile(string FileName, int Position, int Length)
 487         {
 488 
 489 
 490             try
 491             {
 492                 lock (_SyncLockObject)
 493                 {
 494                     string strControlFile = GetControlFileName(FileName);
 495 
 496 
 497                     //if (File.Exists(strControlFile) == false)
 498                     //{
 499                     //    return;
 500                     //}
 501 
 502                     using (StreamWriter sw = new StreamWriter(strControlFile, true, Encoding.Default))
 503                     {
 504                         //sw.NewLine = "$";
 505                         sw.WriteLine(Position.ToString() + "," + Length.ToString() + "," + "0");
 506                     }
 507                     //using (System.IO.FileStream sw = new System.IO.FileStream(strControlFile, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite))
 508                     //{
 509                     //    //sw.Position = e.DownloadState.Position;
 510                     //    sw.Write(Position.ToString() + "," + Length.ToString() + "," + "0"); 
 511                     //    sw.Close();
 512                     //}
 513 
 514 
 515 
 516                 }
 517             }
 518             catch (Exception e)
 519             {
 520                 throw new Exception("写控制文件出错!" + e.Message);
 521             }
 522 
 523         }
 524 
 525         /// <summary>
 526         /// 更新控制文件(Add by ChengKing)
 527         /// </summary>
 528         /// <param name="FileName"></param>
 529         /// <param name="Position"></param>
 530         /// <param name="Length"></param>
 531         private void UpdateControlFile(string FileName, int Position, int Length)
 532         {
 533             try
 534             {
 535                 lock (_SyncLockObject)
 536                 {
 537                     string strControlFile = GetControlFileName(FileName);
 538 
 539 
 540                     //if (File.Exists(strControlFile) == false)
 541                     //{
 542                     //    return;
 543                     //}
 544 
 545                     string s = null;
 546                     using (StreamReader sr = new System.IO.StreamReader(strControlFile))
 547                     {
 548                         s = sr.ReadToEnd();
 549                         s = s.Replace(Position.ToString() + "," + Length.ToString() + "," + "0", Position.ToString() + "," + Length.ToString() + "," + "1");
 550                     }
 551                     using (StreamWriter sw = new StreamWriter(strControlFile, false, Encoding.Default))
 552                     {
 553                         sw.WriteLine(s);
 554                     }
 555                 }
 556             }
 557             catch (Exception e)
 558             {
 559                 throw new Exception("更新控制文件出错!" + e.Message);
 560             }
 561 
 562         }
 563 
 564         /// <summary>
 565         /// 读取所有信息从控制文件(Add by ChengKing)
 566         /// </summary>
 567         /// <param name="FileName"></param>
 568         /// <returns></returns>
 569         private string ReadInfFromControlFile(string FileName)
 570         {
 571             try
 572             {
 573                 lock (_SyncLockObject)
 574                 {
 575                     string strControlFile = GetControlFileName(FileName);
 576 
 577                     string s = null;
 578                     using (StreamReader sr = new System.IO.StreamReader(strControlFile))
 579                     {
 580                         s = sr.ReadToEnd();
 581 
 582                     }
 583                     return s;
 584                 }
 585             }
 586             catch (Exception e)
 587             {
 588                 throw new Exception("读控制文件出错!" + e.Message);
 589             }
 590         }
 591 
 592         /// <summary>
 593         /// 根据目标文件名得到控制文件名(Add by ChengKing)
 594         /// </summary>
 595         /// <param name="FileName"></param>
 596         /// <returns></returns>
 597         public string GetControlFileName(string FileName)
 598         {
 599             string strPath = Path.GetDirectoryName(FileName);
 600 
 601             //string strFileNameWithoutExtension = Path.GetFileNameWithoutExtension(FileName);
 602             string strFileNameWithoutExtension = Path.GetFileName(FileName);
 603             string strControlFile = Path.Combine(strPath, strFileNameWithoutExtension + "_Control.txt");
 604             return strControlFile;
 605         }
 606 
 607         /// <summary>
 608         /// 判断控制文件是否存在
 609         /// </summary>
 610         /// <param name="FileName"></param>
 611         /// <returns></returns>
 612         private bool ExistControlFile(string FileName)
 613         {
 614             string strControlFile = GetControlFileName(FileName);
 615             if (File.Exists(strControlFile))
 616             {
 617                 return true;
 618             }
 619             return false;
 620         }
 621 
 622         /// <summary>
 623         /// 判断控制文件是否完成
 624         /// </summary>
 625         /// <param name="strControlFile"></param>
 626         /// <returns></returns>
 627         public bool JudgeControlFileIfFinished(string strControlFile)
 628         {
 629             try
 630             {
 631                 string s = null;
 632                 lock (_SyncLockObject)
 633                 {
 634                     using (StreamReader sr = new System.IO.StreamReader(strControlFile))
 635                     {
 636                         s = sr.ReadToEnd();
 637                     }
 638                 }
 639                 if (s + String.Empty == String.Empty)
 640                 {
 641                     return false;
 642                 }
 643                 string[] strBlocks = s.Split(new char[2] { '\r''\n' });
 644                 for (int i = 0; i < strBlocks.Length; i++)
 645                 {
 646                     if (strBlocks[i].Trim().Length != 0 && strBlocks[i].Substring(strBlocks[i].Length - 1== "0")
 647                     {
 648                         return false;
 649                     }
 650                 }
 651                 return true;
 652 
 653             }
 654             catch (Exception e)
 655             {
 656                 throw new Exception("判断控制文件是否完成时, 读取文件出错!" + e.Message);
 657             }
 658         }
659 
 660         /// <summary>
 661         /// 删除控制文件(Add by ChengKing)
 662         /// </summary>
 663         /// <param name="strControlFile"></param>
 664         /// <returns></returns>
 665         public bool DeleteControlFile(string strControlFile)
 666         {
 667             try
 668             {
 669                 lock (_SyncLockObject)
 670                 {
 671                     if (File.Exists(strControlFile))
 672                     {
 673                         File.Delete(strControlFile);
 674                     }
 675                 }
 676                 return true;
 677             }
 678             catch (Exception e)
 679             {
 680                 throw new Exception("删除控制文件出错!" + e.Message);
 681             }
 682         }
 683 
 684         #endregion
 685 
 686         /// <summary>
 687         /// 下载一个文件块,利用该方法可自行实现多线程断点续传
 688         /// </summary>
 689         /// <param name="Address">URL 地址</param>
 690         /// <param name="FileName">保存到本地的路径文件名</param>
 691         /// <param name="Length">块大小</param>
 692         public void DownloadFileChunk(string Address, string FileName, int FromPosition, int Length)
 693         {
 694             HttpWebResponse hwrp = null;
 695             string a = null;
 696             try
 697             {
 698                 //this._FileName = FileName;
 699                 HttpWebRequest hwrq = (HttpWebRequest)WebRequest.Create(this.GetUri(Address));
 700                 //hwrq.Credentials = this.m_credentials;
 701 
 702                 hwrq.AddRange(FromPosition);
 703 
 704                 hwrp = (HttpWebResponse)hwrq.GetResponse();
 705 
 706                 //hwrp.Headers.Add("Content-Range", FromPosition.ToString());  //Test
 707 
 708                 a = hwrp.Headers["Content-Disposition"]; //attachment
 709                 if (a != null)
 710                 {
 711                     a = a.Substring(a.LastIndexOf("filename="+ 9);
 712                 }
 713                 else
 714                 {
 715                     a = FileName;
 716                 }
 717 
 718                 byte[] buffer = this.ResponseAsBytes(Address, hwrp, Length, FileName);
 719                 //   lock (_SyncLockObject)
 720                 //   {
 721                 //    this._Bytes += buffer.Length;
 722                 //   }
 723             }
 724             catch (Exception e)
 725             {
 726                 ExceptionActions ea = ExceptionActions.Throw;
 727                 if (this.ExceptionOccurrs != null)
 728                 {
 729                     DownLoadState x = new DownLoadState(Address, hwrp.ResponseUri.AbsolutePath, FileName, a, FromPosition, Length);
 730                     ExceptionEventArgs eea = new ExceptionEventArgs(e, x);
 731                     ExceptionOccurrs(this, eea);
 732                     ea = eea.ExceptionAction;
 733                 }
 734 
 735                 if (ea == ExceptionActions.Throw)
 736                 {
 737                     if (!(e is WebException) && !(e is SecurityException))
 738                     {
 739                         throw new WebException("net_webclient", e);
 740                     }
 741                     throw;
 742                 }
 743             }
 744         }
 745 
 746         internal byte[] ResponseAsBytes(string RequestURL, WebResponse Response, long Length, string FileName)
 747         {
 748             string a = null//AttachmentName
 749             int P = 0//整个文件的位置指针
 750             int num2 = 0;
 751             int num3 = 0;
 752             int intFrom = 0;
 753             try
 754             {
 755                 a = Response.Headers["Content-Disposition"]; //attachment
 756                 if (a != null)
 757                 {
 758                     a = a.Substring(a.LastIndexOf("filename="+ 9);
 759                 }
 760 
 761                 long num1 = Length; //Response.ContentLength;
 762                 bool flag1 = false;
 763                 if (num1 == -1)
 764                 {
 765                     flag1 = true;
 766                     num1 = 0x10000//64k
 767                 }
 768                 byte[] buffer1 = new byte[(long)num1];
 769 
 770 
 771                 int p = 0//本块的位置指针
 772 
 773                 string s = Response.Headers["Content-Range"];
 774                 //string s = hwrq.Headers["Range"];
 775 
 776                 if (s != null)
 777                 {
 778                     s = s.Replace("bytes """);
 779                     s = s.Substring(0, s.IndexOf("-"));
 780                     P = Convert.ToInt32(s);
 781                     intFrom = P;
 782 
 783                 }
 784 
 785                 //int num3 = 0;
 786 
 787                 Stream S = Response.GetResponseStream();
 788 
 789                 int count = 0;
 790 
 791                 int bufferSize = 65535//允许读取的最大字节
 792 
 793                 int times;
 794                 do
 795                 {
 796                     times = 0;
 797 
 798                     //num2 = S.Read(buffer1, num3, ((int)num1) - num3);
 799 
 800                     //限制最大读取字节
 801                     if (bufferSize < ((int)num1) - num3)
 802                     {
 803                         num2 = S.Read(buffer1, num3, bufferSize);
 804                     }
 805                     else
 806                     {
 807                         num2 = S.Read(buffer1, num3, ((int)num1) - num3);
 808                     }
 809 
 810                     //网络短时间的不稳定
 811                     if (num2 == 0)
 812                     {
 813                         Thread.Sleep(50);
 814                         times++;
 815 
 816                         if (times > 100)
 817                         {
 818                             throw new Exception("网络传输层错误");
 819                         }
 820 
 821                     }
 822 
 823                     num3 += num2;
 824                     if (flag1 && (num3 == num1))
 825                     {
 826                         num1 += 0x10000;
 827                         byte[] buffer2 = new byte[(int)num1];
 828                         Buffer.BlockCopy(buffer1, 0, buffer2, 0, num3);
 829                         buffer1 = buffer2;
 830                     }
 831 
 832                     //    lock (_SyncLockObject)
 833                     //    {
 834                     //     this._bytes += num2;
 835                     //    }
 836                     if (num2 > 0)
 837                     {
 838                         if (this.DataReceive != null)
 839                         {
 840                             byte[] buffer = new byte[num2];
 841                             Buffer.BlockCopy(buffer1, p, buffer, 0, buffer.Length);
 842                             DownLoadState dls = new DownLoadState(RequestURL, Response.ResponseUri.AbsolutePath, FileName, a, P, num2, buffer);
 843                             DownLoadEventArgs dlea = new DownLoadEventArgs(dls);
 844 
 845                             //触发事件
 846                             this.OnDataReceive(dlea);
 847                             //System.Threading.Thread.Sleep(100);                            
 848 
 849                         }
 850                         p += num2; //本块的位置指针
 851                         P += num2; //整个文件的位置指针
 852                     }
 853                     else
 854                     {
 855                         break;
 856                     }
 857 
 858                 }
 859                 while (num2 != 0);
 860 
 861                 count++;
 862 
 863                 int c = count;
 864 
 865                 S.Close();
 866                 S = null;
 867                 if (flag1)
 868                 {
 869                     byte[] buffer3 = new byte[num3];
 870                     Buffer.BlockCopy(buffer1, 0, buffer3, 0, num3);
 871                     buffer1 = buffer3;
 872                 }
 873 
 874                 UpdateControlFile(FileName, intFrom, (int)Length);
 875 
 876                 return buffer1;
 877             }
 878             catch (Exception e)
 879             {
 880                 ExceptionActions ea = ExceptionActions.Throw;
 881                 if (this.ExceptionOccurrs != null)
 882                 {
 883                     Thread.Sleep(100);
 884                     //DownLoadState x = new DownLoadState(RequestURL, Response.ResponseUri.AbsolutePath, FileName, a, P, num2);
 885                     //DownLoadState x = new DownLoadState(RequestURL, Response.ResponseUri.AbsolutePath, FileName, a, P, (int)(Length - num3));
 886                     DownLoadState x = new DownLoadState(RequestURL, Response.ResponseUri.AbsolutePath, FileName, a, P, (int)Length);
 887 
 888                     ExceptionEventArgs eea = new ExceptionEventArgs(e, x);
 889                     ExceptionOccurrs(this, eea);
 890                     ea = eea.ExceptionAction;
 891                 }
 892 
 893                 if (ea == ExceptionActions.Throw)
 894                 {
 895                     if (!(e is WebException) && !(e is SecurityException))
 896                     {
 897                         throw new WebException("net_webclient", e);
 898                     }
 899                     throw;
 900                 }
 901                 return null;
 902             }
 903         }

904 
 905         private void OnDataReceive(DownLoadEventArgs e)
 906         {
 907             //触发数据到达事件
 908             DataReceive(this, e);
 909         }
 910 
 911         public byte[] UploadFile(string address, string fileName)
 912         {
 913             return this.UploadFile(address, "POST", fileName, "file");
 914         }
 915 
 916         public string UploadFileEx(string address, string method, string fileName, string fieldName)
 917         {
 918             return Encoding.ASCII.GetString(UploadFile(address, method, fileName, fieldName));
 919         }
 920 
 921         public byte[] UploadFile(string address, string method, string fileName, string fieldName)
 922         {
 923             byte[] buffer4;
 924             FileStream stream1 = null;
 925             try
 926             {
 927                 fileName = Path.GetFullPath(fileName);
 928                 string text1 = "---------------------" + DateTime.Now.Ticks.ToString("x");
 929 
 930                 string text2 = "application/octet-stream";
 931 
 932                 stream1 = new FileStream(fileName, FileMode.Open, FileAccess.Read);
 933                 WebRequest request1 = WebRequest.Create(this.GetUri(address));
 934                 request1.Credentials = this.m_credentials;
 935                 request1.ContentType = "multipart/form-data; boundary=" + text1;
 936 
 937                 request1.Method = method;
 938                 string[] textArray1 = new string[7] { "--", text1, "\r\nContent-Disposition: form-data; name=\"" + fieldName + "\"; filename=\"", Path.GetFileName(fileName), "\"\r\nContent-Type: ", text2, "\r\n\r\n" };
 939                 string text3 = string.Concat(textArray1);
 940                 byte[] buffer1 = Encoding.UTF8.GetBytes(text3);
 941                 byte[] buffer2 = Encoding.ASCII.GetBytes("\r\n--" + text1 + "\r\n");
 942                 long num1 = 0x7fffffffffffffff;
 943                 try
 944                 {
 945                     num1 = stream1.Length;
 946                     request1.ContentLength = (num1 + buffer1.Length) + buffer2.Length;
 947                 }
 948                 catch
 949                 {
 950                 }
 951                 byte[] buffer3 = new byte[Math.Min(0x2000, (int)num1)];
 952                 using (Stream stream2 = request1.GetRequestStream())
 953                 {
 954                     int num2;
 955                     stream2.Write(buffer1, 0, buffer1.Length);
 956                     do
 957                     {
 958                         num2 = stream1.Read(buffer3, 0, buffer3.Length);
 959                         if (num2 != 0)
 960                         {
 961                             stream2.Write(buffer3, 0, num2);
 962                         }
 963                     }
 964                     while (num2 != 0);
 965                     stream2.Write(buffer2, 0, buffer2.Length);
 966                 }
 967                 stream1.Close();
 968                 stream1 = null;
 969                 WebResponse response1 = request1.GetResponse();
 970 
 971                 buffer4 = this.ResponseAsBytes(response1);
 972             }
 973             catch (Exception exception1)
 974             {
 975                 if (stream1 != null)
 976                 {
 977                     stream1.Close();
 978                     stream1 = null;
 979                 }
 980                 if (!(exception1 is WebException) && !(exception1 is SecurityException))
 981                 {
 982                     //throw new WebException(SR.GetString("net_webclient"), exception1);
 983                     throw new WebException("net_webclient", exception1);
 984                 }
 985                 throw;
 986             }
 987             return buffer4;
 988         }
 989 

990           private   byte [] ResponseAsBytes(WebResponse response)
 991          {
 992               int  num2;
 993               long  num1  =  response.ContentLength;
 994               bool  flag1  =   false ;
 995               if  (num1  ==   - 1 )
 996              {
 997                  flag1  =   true ;
 998                  num1  =   0x10000 ;
 999              }
1000               byte [] buffer1  =   new   byte [( int )num1];
1001              Stream stream1  =  response.GetResponseStream();
1002               int  num3  =   0 ;
1003               do
1004              {
1005                  num2  =  stream1.Read(buffer1, num3, (( int )num1)  -  num3);
1006                  num3  +=  num2;
1007                   if  (flag1  &&  (num3  ==  num1))
1008                  {
1009                      num1  +=   0x10000 ;
1010                       byte [] buffer2  =   new   byte [( int )num1];
1011                      Buffer.BlockCopy(buffer1,  0 , buffer2,  0 , num3);
1012                      buffer1  =  buffer2;
1013                  }
1014              }
1015               while  (num2  !=   0 );
1016              stream1.Close();
1017               if  (flag1)
1018              {
1019                   byte [] buffer3  =   new   byte [num3];
1020                  Buffer.BlockCopy(buffer1,  0 , buffer3,  0 , num3);
1021                  buffer1  =  buffer3;
1022              }
1023               return  buffer1;
1024          }
1025 
1026           private  NameValueCollection m_requestParameters;
1027           private  Uri m_baseAddress;
1028           private  ICredentials m_credentials  =  CredentialCache.DefaultCredentials;
1029 
1030           public  ICredentials Credentials
1031          {
1032               get
1033              {
1034                   return   this .m_credentials;
1035              }
1036               set
1037              {
1038                   this .m_credentials  =  value;
1039              }
1040          }
1041 
1042           public  NameValueCollection QueryString
1043          {
1044               get
1045              {
1046                   if  ( this .m_requestParameters  ==   null )
1047                  {
1048                       this .m_requestParameters  =   new  NameValueCollection();
1049                  }
1050                   return   this .m_requestParameters;
1051              }
1052               set
1053              {
1054                   this .m_requestParameters  =  value;
1055              }
1056          }
1057 
1058           public   string  BaseAddress
1059          {
1060               get
1061              {
1062                   if  ( this .m_baseAddress  !=   null )
1063                  {
1064                       return   this .m_baseAddress.ToString();
1065                  }
1066                   return   string .Empty;
1067              }
1068               set
1069              {
1070                   if  ((value  ==   null ||  (value.Length  ==   0 ))
1071                  {
1072                       this .m_baseAddress  =   null ;
1073                  }
1074                   else
1075                  {
1076                       try
1077                      {
1078                           this .m_baseAddress  =   new  Uri(value);
1079                      }
1080                       catch  (Exception exception1)
1081                      {
1082                           throw   new  ArgumentException( " value " , exception1);
1083                      }
1084                  }
1085              }
1086          }
1087 
1088           public  Uri GetUri( string  path)
1089          {
1090              Uri uri1;
1091               try
1092              {
1093                   if  ( this .m_baseAddress  !=   null )
1094                  {
1095                      uri1  =   new  Uri( this .m_baseAddress, path);
1096                  }
1097                   else
1098                  {
1099                      uri1  =   new  Uri(path);
1100                  }
1101                   if  ( this .m_requestParameters  ==   null )
1102                  {
1103                       return  uri1;
1104                  }
1105                  StringBuilder builder1  =   new  StringBuilder();
1106                   string  text1  =   string .Empty;
1107                   for  ( int  num1  =   0 ; num1  <   this .m_requestParameters.Count; num1 ++ )
1108                  {
1109                      builder1.Append(text1  +   this .m_requestParameters.AllKeys[num1]  +   " = "   +   this .m_requestParameters[num1]);
1110                      text1  =   " & " ;
1111                  }
1112                  UriBuilder builder2  =   new  UriBuilder(uri1);
1113                  builder2.Query  =  builder1.ToString();
1114                  uri1  =  builder2.Uri;
1115              }
1116               catch  (UriFormatException)
1117              {
1118                  uri1  =   new  Uri(Path.GetFullPath(path));
1119              }
1120               return  uri1;
1121          }
1122 
1123      }
1124 
1125  }
1126 
1127 

    2.页面 Default.aspx 文件 代码

 1  < html xmlns = " http://www.w3.org/1999/xhtml "   >
 2  < head runat = " server " >
 3       < script language = " javascript " >
 4          mainLoop  =  function()
 5          {
 6              var objPath  =  document.getElementById( " TextBox2 " ); 
 7              var blnValue  =  _Default.CheckControlFiles(objPath.value);                       
 8               // a.value = a.value + blnValue.value;
 9               if ( blnValue.value  ==   true )
10              {
11                  var returnvalue = setTimeout( ' mainLoop() ' 1000 );            
12              }
13               else
14              {
15                  var objStatus  =  document.getElementById( " Label1 " ); 
16                  objStatus.innerText  =   " 状态: 下载完成! " ;
17                  
18                  var btOK  =  document.getElementById( " btOK " );
19                  btOK.disabled  =   "" ;              
20                  
21                  var btCancel  =  document.getElementById( " btCancel " );
22                  btCancel.disabled  =   " disabled " ;
23                                  
24              }
25          }    
26          
27      
28       </ script >
29 
30  </ head >
31  < body >
32       < form id = " frmTest "  runat = " server "   >
33       < table bgcolor = " #ffcc66 " >< tr >< td style = " height: 259px " >
34           < br  />
35           < strong >< span style = " color: #000099 " >
36          下载组件:
37           < br  />
38           1 . 支持多线程:  & nbsp;多个线程某时刻下载同一个文件的不同块. < br  />
39           2 . 断点续传:  & nbsp;如果下载了一个文件的某些块(一半), 则下次 < br  />
40               & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp; 下载时只需下载未完成的块;
41               & nbsp;文件块的下载状 < br  />
42               & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp; 态用控制文件记录.
43              块下载完成的先后顺序不 < br  />
44               & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp;  & nbsp; 一定是连续的. < br  />
45           </ span ></ strong >
46           < br  />
47           < table style = " width: 379px " >
48               < tr >
49                   < td colspan = " 1 "  style = " width: 87px " >
50                      Source </ td >
51                   < td colspan = " 2 "  style = " width: 326px " >
52           < asp:TextBox ID = " TextBox1 "  runat = " server "  Width = " 391px " > http: // www.</asp:TextBox></td>
53               </ tr >
54               < tr >
55                   < td colspan = " 1 "  style = " width: 87px " >
56                      Location </ td >
57                   < td colspan = " 2 "  style = " width: 326px " >
58                       < asp:TextBox ID = " TextBox2 "  runat = " server "  Width = " 391px " > D:\Documents and Settings\zhengjian\桌面\TestDownLoads\ </ asp:TextBox ></ td >
59               </ tr >
60               < tr >
61                   < td colspan = " 1 "  style = " width: 87px " >
62                      Threads </ td >
63                   < td colspan = " 2 "  style = " width: 326px " >
64                       < asp:TextBox ID = " TextBox3 "  runat = " server "  Width = " 390px " > 10 </ asp:TextBox ></ td >
65               </ tr >
66           </ table >
67           < br  />
68           < asp:Button ID = " btOK "  runat = " server "  Text = " 下载 "  Height = " 42px "  Width = " 108px "  OnClick = " btOK_Click "   />& nbsp;
69           & nbsp; < asp:Button ID = " btCancel "  runat = " server "  OnClick = " btCancel_Click "  Text = " 取消/暂停 "  Height = " 42px "  Width = " 108px "  Enabled = " False "   />< br  />
70           < br  />
71           < asp:Label ID = " Label1 "  runat = " server "  Height = " 32px "  Text = " 状态: 未开始下载 "  Width = " 227px "  Font - Bold = " True "  ForeColor = " #8080FF " ></ asp:Label >< br  />
72           </ td ></ tr ></ table >
73           < script language = javascript >
74              mainLoop();
75           </ script >
76       </ form >
77  </ body >
78  </ html >

   3. 页面后台文件 Default.aspx.cs代码

  1  ///   <summary>
  2  ///  Author: [ ChengKing(ZhengJian) ] 
  3  ///  Blog:   Http://blog.youkuaiyun.com/ChengKing
  4  ///  注:从网上找了个优秀代码
  5  ///  扩展如下功能: 
  6  ///    1. 解决一些线程相关的Bug; 
  7  ///    2.扩展用控制文件实现断点续传功能.
  8  ///   </summary>
  9  public  partial  class  _Default : System.Web.UI.Page 
 10  {    
 11       // 全局变量
 12       private   static   object  _SyncLockObject  =   new   object ();
 13 
 14       protected   void  Page_Load( object  sender, EventArgs e)
 15      {
 16          Utility.RegisterTypeForAjax( typeof (_Default));       
 17           // this.TextBox1.Text = " http://download.youkuaiyun.com/filedown/aHR0cDovL2Rvd25sb2FkMS5jc2RuLm5ldC9kb3duMy8yMDA3MDUwNy8wNzE4MDIwNzY4OC5yYXI= !177258";
 18          
 19          
 20           this .TextBox1.Text  =   " http://files.cnblogs.com/ChengKing/智能象棋游戏(T1).rar " ;
 21      }
 22       protected   void  btOK_Click( object  sender, EventArgs e)
 23      {
 24           this .Label1.Text  =   " 状态: 正在下载 " ;
 25          
 26          DownLoadComponent.HttpWebClient x  =   new  DownLoadComponent.HttpWebClient();
 27 
 28           // 注册 DataReceive 事件
 29          x.DataReceive  +=   new  DownLoadComponent.HttpWebClient.DataReceiveEventHandler( this .x_DataReceive);
 30           // 注册 ExceptionOccurrs 事件
 31          x.ExceptionOccurrs  +=   new  DownLoadComponent.HttpWebClient.ExceptionEventHandler( this .x_ExceptionOccurrs);
 32 
 33           string  Source  =   this .TextBox1.Text.Trim();
 34           string  FileName  =  Source.Substring(Source.LastIndexOf( " / " +   1 );
 35           string  Location =  System.IO.Path.Combine(  this .TextBox2.Text.Trim() , FileName);
 36          
 37           // F: 源服务器文件;  _f: 保存路径;  10: 自设定一个文件有几个线程下载.
 38          x.DownloadFile(Source,Location ,  int .Parse( this .TextBox3.Text));
 39 
 40           // Response.Write("正在下载文件");
 41           this .btOK.Enabled  =   false ;
 42           this .btCancel.Enabled  =   true ;
 43      }
 44 
 45       private   void  x_DataReceive(DownLoadComponent.HttpWebClient Sender, DownLoadComponent.DownLoadEventArgs e)
 46      {
 47 
 48           string  f  =  e.DownloadState.FileName;
 49           if  (e.DownloadState.AttachmentName  !=   null )
 50              f  =  System.IO.Path.GetDirectoryName(f)  +   @" \ "   +  e.DownloadState.AttachmentName;       
 51          
 52           using  (System.IO.FileStream sw  =   new  System.IO.FileStream(f, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite, System.IO.FileShare.ReadWrite))
 53          {
 54              sw.Position  =  e.DownloadState.Position;                
 55              sw.Write(e.DownloadState.Data,  0 , e.DownloadState.Data.Length); 
 56              sw.Close();
 57          }            
 58      }   
 59 
 60       private   void  x_ExceptionOccurrs(DownLoadComponent.HttpWebClient Sender, DownLoadComponent.ExceptionEventArgs e)
 61      {
 62          System.Console.WriteLine(e.Exception.Message);
 63           // 发生异常重新下载相当于断点续传,你可以自己自行选择处理方式或自行处理
 64          DownLoadComponent.HttpWebClient x  =   new  DownLoadComponent.HttpWebClient();
 65          x.DataReceive  +=   new  DownLoadComponent.HttpWebClient.DataReceiveEventHandler( this .x_DataReceive);
 66           // 订阅 ExceptionOccurrs 事件
 67           // x.ExceptionOccurrs += new DownLoadComponent.HttpWebClient.ExceptionEventHandler(this.x_ExceptionOccurrs);
 68 
 69          x.DownloadFileChunk(e.DownloadState.RequestURL, e.DownloadState.FileName, e.DownloadState.Position, e.DownloadState.Length);
 70          e.ExceptionAction  =  DownLoadComponent.ExceptionActions.Ignore;
 71      }
 72       protected   void  btCancel_Click( object  sender, EventArgs e)
 73      {
 74           if  (DownLoadComponent.HttpWebClient.threads  !=   null )
 75          {
 76               foreach  (Thread t  in  DownLoadComponent.HttpWebClient.threads)
 77              {
 78                   if  (t.IsAlive)
 79                  {
 80                      t.Abort();
 81                  }
 82              }
 83 
 84              DownLoadComponent.HttpWebClient.threads.Clear();
 85          }
 86          System.Diagnostics.Process myproc  =   new  System.Diagnostics.Process();
 87          Process[] procs  =  (Process[])Process.GetProcessesByName( " DW20.exe " );   // 得到所有打开的进程
 88           try
 89          {
 90               foreach  (Process proc  in  procs)
 91              {
 92                   if  (proc.CloseMainWindow()  ==   false )
 93                  {
 94                      proc.Kill();
 95                  }
 96              }
 97          }
 98           catch
 99          { }
100          KillAllThreads();
101           this .btOK.Enabled  =   true ;
102           this .btCancel.Enabled  =   false ;
103          GC.Collect();
104         
105      }
106 
107       ///   <summary>
108       ///  定期检查控制文件
109       ///   </summary>
110       ///   <param name="str"></param>
111       ///   <returns> 是否还继续监视(1: 正在下载中,继续监视; 0: 表示已经下载完毕,不用再检视) </returns>
112      [AjaxMethod()] //  or [AjaxPro.AjaxMethod] 
113       public   bool  CheckControlFiles( string  strObjPath)
114      {
115           if  ( ! WhetherDownloadFinished(strObjPath))
116          {
117               return   true ;
118          }
119           return   false ;
120      }
121 
122       private   bool  WhetherDownloadFinished( string  strObjPath)
123      {
124          DirectoryInfo df  =   new  DirectoryInfo(strObjPath);
125          FileInfo[] fi  =  (FileInfo[])df.GetFiles( " *.txt " , SearchOption.TopDirectoryOnly);
126          HttpWebClient hwc  =   new  HttpWebClient();        
127           for  ( int  i  =   0 ; i  <  fi.Length; i ++ )
128          {
129               if  (fi[i].FullName.Length  >   12   &&  fi[i].FullName.Substring(fi[i].FullName.Length  -   12 ==   " _Control.txt " )
130              {                
131                   if  (hwc.JudgeControlFileIfFinished(fi[i].FullName)  ==   true )
132                  {
133                      hwc.DeleteControlFile(fi[i].FullName);
134                      KillAllThreads();
135                       return   true ;
136                  }
137              }
138          }
139           return   false ;
140      }
141 
142       private   void  KillAllThreads()
143      {
144           foreach  (Thread t  in  HttpWebClient.threads)
145          {
146               if  (t.IsAlive)
147              {
148                  t.Abort();
149              }
150          }
151          HttpWebClient.threads.Clear();
152      }
153 
154  }
155 

 

(四).示例代码下载

       http://files.cnblogs.com/MVP33650/MultiThreadDownLoadFile.rar

(五).Asp.net 2.0其它相关文章:     

           http://blog.youkuaiyun.com/ChengKing/category/288694.aspx

转载于:https://www.cnblogs.com/hdjjun/archive/2008/06/17/1223891.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值