因為有些人可能會疑惑,講了這麼多 多線程,到底在實際的應用上有什麼作用的呢? 這裡我在這裡用多線程簡單實作了一個檔案的下載下傳的功能。
伺服器端頁面:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="FileServer.Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Image ID="Image1" runat="server" ImageUrl="~/Images/1.gif" />
說明: CLR Via C#
</div>
</form>
</body>
</html>
伺服器頁面隻是一個簡單顯示需要下載下傳檔案的一些資訊,這裡通過Handler.ashx來處理檔案的下載下傳,把檔案的轉化為二進制位元組寫入到輸出流中,具體實作代碼為:
public class Handle : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
HttpResponse response = context.Response;
HttpRequest request = context.Request;
FileStream fileStream = null;
byte[] buffer = new Byte[10240];
int length;
// 剩餘的位元組大小
// 因為這裡采取的是每次寫入10240位元組到輸出流中
long readToData;
try
{
string filename = "CLR via CSharp 3rd edition.pdf"; //通過解密得到檔案名
string filepath = HttpContext.Current.Server.MapPath("~/") + "Resources/" + filename; //待下載下傳的檔案路徑
fileStream = new FileStream(filepath, FileMode.Open,FileAccess.Read, FileShare.Read);
readToData = fileStream.Length;
while (readToData > 0)
{
// 實際讀取的位元組大小
length = fileStream.Read(buffer, 0, buffer.Length);
// 把讀取到的位元組寫入輸出流中
response.OutputStream.Write(buffer, 0, length);
response.Flush();
readToData = readToData - length;
}
}
catch (Exception ex)
response.Write("Error:" + ex.Message);
finally
if (fileStream != null)
fileStream.Close();
response.End();
}
public bool IsReusable
get
return false;
}
這裡牽涉到HttpHandle對象問題,這個對象在Asp.net中是真正處理資料的對象,後面如果有時間也和大家分享下深入了解Asp.net系列,主要是介紹在Asp.net中一些核心對象為我們默默做的一些事情,在這裡也不詳細介紹HttpHandle對象了, 這個示例中主要通過這個類來對檔案的處理,把檔案的二進制位元組寫入到輸出流中, 用戶端在從輸出流中讀取位元組,然後儲存為檔案(其實檔案也就是“流”)。
用戶端:
用戶端建立了一個WinForm視窗,通過WebBrower控件(就是在WinForm程式中顯示網頁的控件)來連接配接伺服器頁面,當按下下載下傳按鈕後,通過線程池線程來執行下載下傳方法。主要代碼為:
public void DownLoad(object state)
// 計時對象
Stopwatch sw = Stopwatch.StartNew();
HttpWebRequest request;
HttpWebResponse response;
Stream stream;
// 下載下傳下來的儲存的位址
string savepath = "D:\\Download.pdf";
FileStream savestream = new FileStream(savepath, FileMode.OpenOrCreate);
// 送出請求
request = (HttpWebRequest)HttpWebRequest.Create(url);
// 獲得回應對象
response = (HttpWebResponse)request.GetResponse();
// 獲得回應流
stream = response.GetResponseStream();
byte[] bytes = new byte[10240];
int readsize;
// 每次都讀取10240位元組
// 采用的是同步讀取方法
// 計算耗費的時間
readsize = stream.Read(bytes, 0, bytes.Length);
while (readsize > 0)
savestream.Write(bytes, 0, readsize);
readsize = stream.Read(bytes, 0, bytes.Length);
sw.Stop();
MessageBox.Show("下載下傳耗時為:" + sw.Elapsed.ToString(), "提示");
MessageBox.Show(ex.Message, "Error");
savestream.Close();
}
這樣就利用線程池線程簡單完成了用戶端下載下傳伺服器端檔案的功能,并且使用線程池線程這樣不會阻塞主線程,進而導緻在下載下傳檔案時,界面同樣可以操作,如果不采用多線程操作的話将會在下載下傳過程導緻界面“卡死”現象,這樣就會給使用者帶來不好的使用者體驗。
其實本來還想做複雜點的, 開始想實作的功能,是伺服器端斷點續傳,然後用戶端多線程下載下傳的功能的,這個示例中隻用到了一個線程池線程來完成下載下傳任務,本來想通過執行多個線程池線程來完成下載下傳任務的, 每個線程隻負責一部分的讀取工作, 然後把每個線程中讀取的位元組合并起來就是完整的檔案位元組了,但是這裡遇到一個問題,怎麼在伺服器端實作續傳的功能的, 用戶端通過AddRange方法來發出部分讀取請求,然後伺服器端就要對請求頭Range進行解析的,實作原理我還是清楚,但是在做的過程中還是出現了問題。是以這裡隻能分享一個簡單的下載下傳檔案的功能給大家了, 至于多線程的下載下傳和斷點續傳和大檔案的上傳等問題,等我學習了再和大家分享, 如果有大牛可以幫助我解決服務端斷點續傳的問題的話,歡迎留言。
本文轉自LearningHard 51CTO部落格,原文連結:http://blog.51cto.com/learninghard/1034798,如需轉載請自行聯系原作者