C#实现http多线程下载文件
2016-05-30 23:49
627 查看
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.IO; using System.Net; using u32 = System.UInt32; using s32 = System.Int32; using f32 = System.Single; namespace Test { class Program { static readonly string mUrl = "http://127.0.0.1:8080//download"; public class CDownloadFile { public CDownloadFile( string FileName, u32 FileSize ) { mFileName = FileName; mFileSize = FileSize; } public string mFileName; public u32 mFileSize; } public class CThreadParam { public s32 mId; public string mFileName; public s32 mBeginBlock; public u32 mEndBlock; public ManualResetEvent mStartEvent; public ManualResetEvent mDoneEvent; } static void Main(string[] args) { //获取待下载文件大小本例就不写了比较简单略过,重点讨论下载部分 //如存在下面待下载的文件 List<CDownloadFile> DownloadFiles = new List<CDownloadFile>(); DownloadFiles.Add(new CDownloadFile("1.rmvb", 123456)); DownloadFiles.Add(new CDownloadFile("2.rmvb", 454578)); //cpu核心数为线程数 ManualResetEvent[] StartEvents = new ManualResetEvent[Environment.ProcessorCount]; ManualResetEvent[] DoneEvents = new ManualResetEvent[Environment.ProcessorCount]; CThreadParam[] Params = new CThreadParam[Environment.ProcessorCount]; Thread[] Threads = new Thread[Environment.ProcessorCount]; for (s32 i = 0; i < Environment.ProcessorCount; ++i) { StartEvents[i] = new ManualResetEvent(false); DoneEvents[i] = new ManualResetEvent(false); Params[i] = new CThreadParam(); Params[i].mId = i;//线程ID Params[i].mStartEvent = StartEvents[i];//启动下载事件 Params[i].mDoneEvent = DoneEvents[i];//完成下载事件 Threads[i] = new Thread(new ParameterizedThreadStart(AsyncDownloadFile)); Threads[i].Start(Params[i]); } //始终只有Environment.ProcessorCount多个线程在下载一个文件,你可以根据实际情况修改 for (s32 i = 0; i < DownloadFiles.Count; ++i) { CDownloadFile f = DownloadFiles[i]; //分配线程下载任务 CutFile(Params, f.mFileSize); for (s32 j = 0; j < Environment.ProcessorCount; ++j) { Params[j].mFileName = f.mFileName; Params[j].mDoneEvent.Reset();//清空下载完成 Params[j].mStartEvent.Set();//启动线程 } //等待下载中... WaitHandle.WaitAll(DoneEvents,-1); //将临时文件合并为完整文件 MergeFile(Params); Console.WriteLine(f.mFileName + "下载完毕"); } } //根据线程数量拆分文件大小计算每个线程下载文件大小块 static void CutFile(CThreadParam[] Param, u32 FileSize) { //伐值,文件大小小于10MB没必要用多个线程下,根据实际情况调整 if (FileSize < 1024 * 1024 * 10) { //从第0个字节下载到FileSize-1个字节,即整个文件大小 Param[0].mBeginBlock = 0; Param[0].mEndBlock = FileSize-1; //其他的线程就设置一个标记表示没有启用 for (s32 i = 1; i < Param.Length; ++i) { Param[i].mBeginBlock = -1; } } else { //块大小 u32 Block = FileSize / (u32)Param.Length; //每个线程计算自己的下载量 for (u32 i = 0; i < Param.Length; ++i) { Param[i].mBeginBlock = (s32)(i * Block); Param[i].mEndBlock = (u32)Param[i].mBeginBlock + Block - 1; } //如果存在余数分配给最后一个线程 Param[Param.Length - 1].mEndBlock += FileSize % (u32)Param.Length; } } static void AsyncDownloadFile(Object o) { var Param = (CThreadParam)o; HttpWebRequest Request; HttpWebResponse Response; Stream HttpStream; FileStream OutStream; s32 ReadBytes = 0; Byte[] Buffer = new Byte[1024]; while (true) { Param.mStartEvent.WaitOne(); if (-1 == Param.mBeginBlock) { Param.mStartEvent.Reset(); Param.mDoneEvent.Set(); continue; } try { //请求 Request = WebRequest.Create(mUrl + "//" + Param.mFileName) as HttpWebRequest; //调整服务器上面文件指针(需要文件http服务器支持) //题外话如果是支持断点续传也是调整这个值,判断临时文件是否存在即可,写本地文件的时候将文件指针指向最后即可(本例不讨论) Request.AddRange(Param.mBeginBlock, Param.mEndBlock); //得到响应 Response = Request.GetResponse() as HttpWebResponse; //Response.StatusCode; 这个值为200表示一切OK HttpStream = Response.GetResponseStream(); //写入临时文件 OutStream = new FileStream( Param.mFileName + Param.mId.ToString(), FileMode.Create ); while (true) { ReadBytes = HttpStream.Read(Buffer, 0, Buffer.Length); if (ReadBytes <= 0) { break; } OutStream.Write(Buffer, 0, ReadBytes); } OutStream.Close(); HttpStream.Close(); Response.Close(); Request.Abort(); Param.mStartEvent.Reset(); Param.mDoneEvent.Set(); } catch (WebException e) { //自行处理异常 Console.WriteLine(e.Message); } catch (Exception e) { //自行处理异常 Console.WriteLine(e.Message); } } } static void MergeFile( CThreadParam[] Param ) { //只有一个临时文件,即是完整文件,改掉文件名即可 if (-1 == Param[1].mBeginBlock) { if (File.Exists(Param[0].mFileName)) { File.Delete(Param[0].mFileName); } File.Move(Param[0].mFileName + Param[0].mId.ToString(), Param[0].mFileName); return; } FileStream OutStream = new FileStream( Param[0].mFileName, FileMode.Create ); FileStream InStream; Byte[] Buffer = new Byte[1024]; s32 ReadBytes = 0; for (u32 i = 0; i < Param.Length; ++i) { //读取临时文件 InStream = new FileStream(Param[i].mFileName + Param[i].mId.ToString(), FileMode.Open); while (true) { ReadBytes = InStream.Read(Buffer, 0, Buffer.Length); if (ReadBytes <= 0) { break; } OutStream.Write(Buffer, 0, ReadBytes); } InStream.Close(); //删除临时文件 File.Delete(Param[i].mFileName + Param[i].mId.ToString()); } OutStream.Close(); } } }
相关文章推荐
- RPC failed; result=22, HTTP code = 411
- Python3写爬虫(四)多线程实现数据爬取
- Scrapy的架构介绍
- 发布一个自己做的jsp博客系统
- c#调用COM组件
- HTTP Header 属性列表
- nginx中http核心模块的配置指令2
- nginx中http核心模块的配置指令3
- nginx中http核心模块的配置指令4
- nginx中http的fastcgi模块的配置指令1
- 如何在 Linux 中快速地通过 HTTP 提供文件访问服务
- 提供个 全免杀海洋2006asp木马 下载
- 高手写的Tracer-Flash代码调试类代码下载
- Vista 防火墙 Vista Firewall Control v1.0.11 下载
- C#实现把指定数据写入串口
- C#动态创建button的方法
- 国外Lightbox v2.03.3 最新版 下载