网站首页
网站导航
Ctrl+D收藏
首 页
代码段
源码包
文档库
工具箱
代码语言
.
CSharp
.
JS
Java
Asp.Net
C
MSSQL
PHP
Css
PLSQL
Python
Shell
EBS
ASP
Perl
ObjC
VB.Net
VBS
MYSQL
GO
Delphi
AS
DB2
Domino
Rails
ActionScript
Scala
代码分类
文件
系统
字符串
数据库
网络相关
图形/GUI
多媒体
算法
游戏
Jquery
Extjs
Android
HTML5
菜单
网页交互
WinForm
控件
企业应用
安全与加密
脚本/批处理
开放平台
其它
【
C#
】
实现http多线程下载文件
作者:
/ 发布于
2018/1/2
/
274
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(); } } }
评论列表
本站所提供的代码,版权归原作者所有,若有侵犯作者版权,请与我们联系,我们将立即删除或修改。谢谢!
本站所有代码发布及提供者。
试试其它关键字
同语言下
.
C#动态修改文件夹名称(FSO实现,不移动文件)
.
c# 移动文件或文件夹
.
c#图片添加水印
.
网站后台修改图片尺寸代码
.
处理大图片在缩略图时的展示
.
实现对图片上传的接收
.
去除字符串中的空格,回车,换行符转变成‘;’在按‘
.
按照回车换行符分割字符串
.
文件MD5码 比较,检测文件是否一样
.
计算每周和每月固定日期
可能有用的
.
C#动态修改文件夹名称(FSO实现,不移动文件)
.
c# 移动文件或文件夹
.
c#图片添加水印
.
网站后台修改图片尺寸代码
.
处理大图片在缩略图时的展示
.
实现对图片上传的接收
.
去除字符串中的空格,回车,换行符转变成‘;’在按‘
.
按照回车换行符分割字符串
.
文件MD5码 比较,检测文件是否一样
.
计算每周和每月固定日期
贡献的其它代码
Label
地图
本站
我们
服务
版权
联系
回馈
博客