您的位置:首页 > 理论基础 > 计算机网络

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();
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C# http 多线程 下载