(一) . 概述
最近做了个C/S文件下载工具, 支持多任务, 多线程和断点续传功能. 其中部分代码是从网上找来的, 自己改了
许多Thread Bug, 并增加多任务, 断点续传等功能.
由于公司具有代码所有权, 不能将源代码共享. 自己对比较Asp.net感兴趣, 业余时间自己做了个简单的, 基于
Asp.net 2.0的, 目前能够执行对一个文件的下载任务, 但已经实现了多线程, 断点续传功能. 根据需要您可以增加
多任务 功能, 分享一下, 互相学习! 互相借鉴!
时间仓促, 此程序还没有做很多参数方面的优化. 可以作参考用.
(二).运行效果
(三). 代码
1. 核心 DownLoadState.cs 文件代码
2 /// Author: [ ChengKing(ZhengJian) ]
3 /// Blog: Http://blog.csdn.net/ChengKing
4 /// 注:从网上找了个优秀代码
5 /// 扩展如下功能:
6 /// 1. 解决一些线程相关的Bug;
7 /// 2.扩展用控制文件实现断点续传功能.
8 ///
9 namespace DownLoadComponent
10 {
11 ///
12 /// 多线程辅助类(Add by ChengKing)
13 ///
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 ///
63 /// 记录下载的字节位置
64 ///
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 ///
193 /// 包含 Exception 事件数据的类
194 ///
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 ///
238 /// 包含 DownLoad 事件数据的类
239 ///
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 ///
260 /// 支持断点续传多线程下载的类
261 ///
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 ///
287 /// 分块下载文件
288 ///
289 /// URL 地址
290 /// 保存到本地的路径文件名
291 /// 块数,线程数
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 ///
481 /// 插入文件块信息到控制文件(Add by ChengKing)
482 ///
483 ///
484 ///
485 ///
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 ///
526 /// 更新控制文件(Add by ChengKing)
527 ///
528 ///
529 ///
530 ///
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 ///
565 /// 读取所有信息从控制文件(Add by ChengKing)
566 ///
567 ///
568 ///
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 ///
593 /// 根据目标文件名得到控制文件名(Add by ChengKing)
594 ///
595 ///
596 ///
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 ///
608 /// 判断控制文件是否存在
609 ///
610 ///
611 ///
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 ///
623 /// 判断控制文件是否完成
624 ///
625 ///
626 ///
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 ///
661 /// 删除控制文件(Add by ChengKing)
662 ///
663 ///
664 ///
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 ///
687 /// 下载一个文件块,利用该方法可自行实现多线程断点续传
688 ///
689 /// URL 地址
690 /// 保存到本地的路径文件名
691 /// 块大小
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
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 文件 代码
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.
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代码
2 /// Author: [ ChengKing(ZhengJian) ]
3 /// Blog: Http://blog.csdn.net/ChengKing
4 /// 注:从网上找了个优秀代码
5 /// 扩展如下功能:
6 /// 1. 解决一些线程相关的Bug;
7 /// 2.扩展用控制文件实现断点续传功能.
8 ///
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.csdn.net/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 ///
108 /// 定期检查控制文件
109 ///
110 ///
111 ///
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.csdn.net/ChengKing/category/288694.aspx