標題: C#多線程Http協議下載文件 [打印本頁]
作者: hongniu 時間: 2015-6-25 16:36
標題: C#多線程Http協議下載文件
本文是參照博客園【CAP&船長】的Demo衍生出來的。
主要是C#通過HttpWebRequest類通過Http協議并調用多線程下載Web資源。
思路:
1、獲取文件的總大小
2、設置啟動的線程數目,并分配每個線程的下載的開始位置與下載的字節數大小
3、子線程下載成功后保存到臨時文件中
4、監聽所有文件是否全部下載成功
5、如果所有子線程均下載完成則根據現在Id拼接起來
6、刪除臨時文件
簡單封裝的類:
1、新建一個【CHttpDownload.cs】類
2、聲明必須的公共屬性與私有屬性以及變量
private int nThreadNum = 0; // 線程的個數 private string strUrl = ""; // 下載地址
private string strSavePath = ""; // 文件保存地址
private System.Threading.Thread[] threads = null; // 線程數組
private int[] nStartPosition = null; // 每個線程的開始地址
private int[] nFileSize = null; //每個線程下載的大小
private bool[] bThreadState = null; // 線程下載狀態
public bool bHasMerge { get; private set; } // 是否合并成功
public long lFileSizeAll { get; private set; } // 文件總大小
public int nBufferSize { get; set; } // 下載文件緩沖區大小
private int id = 0; // 標記線程Id、臨時變量 2、構造函數 /// <summary>
/// 構造函數
/// </summary>
/// <param name="strUrl">文件下載地址Url</param>
/// <param name="nThreadNum">所需要的線程數</param>
public CHttpDownload(string strUrl, int nThreadNum)
{
/* 初始化下載地址、線程數、緩沖流大小 */
this.strUrl = strUrl;
this.nThreadNum = nThreadNum;
this.nBufferSize = 1024;
Init();
} /// <summary>
/// 初始化數據、設置每個線程的開始字節位置與字節大小
/// </summary>
private void Init()
{
threads = new System.Threading.Thread[this.nThreadNum];
nStartPosition = new int[this.nThreadNum];
nFileSize = new int[this.nThreadNum];
bThreadState = new bool[this.nThreadNum];
bHasMerge = false;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(this.strUrl);
lFileSizeAll = request.GetResponse().ContentLength; // 獲取文件的總大小
request.Abort(); // 端口本次連接
int nFileNormalSize = (int)lFileSizeAll / nThreadNum; // 前面線程下載的大小,最后一個線程下載全部
int nFileLastSize = nFileNormalSize + (int)lFileSizeAll % nThreadNum; // 最后一個線程下載的大小
for (int i = 0; i < nThreadNum; i++)
{
bThreadState[i] = false;
nStartPosition[i] = nFileNormalSize * i;
if (i == nThreadNum - 1)
{
nFileSize[i] = nFileLastSize - 1;
}
else
{
nFileSize[i] = nFileNormalSize - 1;
}
}
}
3、新建StartReceive方法,功能為啟動指定一個線程進行下載,并保存到臨時文件中 /// <summary>
/// 多線程開始接收
/// </summary>
private void StartReceive()
{
id++;
int nId = id - 1;
string strFileName = "C:\\\\" + nId + ".tmp";
byte[] buffer = new byte[nBufferSize];
int nReadSize = 0; // 標記本次讀到多少個數據
FileStream fs = new FileStream(strFileName, System.IO.FileMode.Create);
Stream s = null;
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(this.strUrl);
request.AddRange(nStartPosition[nId], nStartPosition[nId] + nFileSize[nId]); // 設置流的開始位置與結束位置
s = request.GetResponse().GetResponseStream();
nReadSize = s.Read(buffer, 0, nBufferSize);
while (nReadSize > 0)
{
fs.Write(buffer, 0, nReadSize);
nReadSize = s.Read(buffer, 0, nBufferSize);
}
fs.Close();
s.Close();
}
catch (Exception ex)
{
}
bThreadState[nId] = true;
}
4、新建MergeFiles方法,功能為當所有線程完成下載后進行文件合并保存、并刪除臨時文件
/// <summary> /// 合并文件
/// </summary>
private void MergeFiles()
{
while (true) // 驗證所有線程是否全部接收完畢
{
bHasMerge = true;
for (int i = 0; i < nThreadNum; i++)
{
if (bThreadState[i] == false)
{
bHasMerge = false;
System.Threading.Thread.Sleep(100);
break;
}
}
if (bHasMerge == true)
{
break;
}
}
int nReadSize;
byte[] bytes = new byte[nBufferSize];
FileStream fs = new FileStream(strSavePath, FileMode.Create);
FileStream fsTmp = null;
for (int i = 0; i < nThreadNum; i++)
{
fsTmp = new FileStream("C:\\\\" + i + ".tmp", FileMode.Open);
while (true)
{
nReadSize = fsTmp.Read(bytes, 0, nBufferSize);
if (nReadSize > 0)
{
fs.Write(bytes, 0, nReadSize);
}
else
{
break;
}
}
fsTmp.Close();
if (File.Exists("C:\\\\" + i + ".tmp")) // 如果臨時文件存在則刪除
{
File.Delete("C:\\\\" + i + ".tmp");
}
}
fs.Close();