之前學習了一部分的C#基礎,但是感覺會的不多,很多地方依然需要通過做一點小Demo來進行鞏固,那麼這個C#的網路下載器,就來了 原理講解 首先我們編寫代碼之前,我們需要瞭解下網路下載的原理到底是什麼? 學習過C#中IO流部分的知識,或者你有其它的語言的基礎,學習過其它語言的文件IO的基礎,肯定瞭解過 ...
之前學習了一部分的C#基礎,但是感覺會的不多,很多地方依然需要通過做一點小Demo來進行鞏固,那麼這個C#的網路下載器,就來了
原理講解
首先我們編寫代碼之前,我們需要瞭解下網路下載的原理到底是什麼?
學習過C#中IO流部分的知識,或者你有其它的語言的基礎,學習過其它語言的文件IO的基礎,肯定瞭解過我們電腦中的數據都是二進位,那麼網路中傳輸的數據本質上也是一樣的。
我們在學習文件IO中,都會學習文件的讀寫操作,讀操作(Output),將文件中的二進位數據讀出來,寫操作(Input)將記憶體中的二進位數據寫入到硬碟中的文件中。
那麼網路下載的本質就是文件的讀寫,其步驟分為以下幾步:
- 向伺服器發起請求
- 伺服器接收到請求,返迴響應,而這個響應是一個文件流數據
- 程式接收到響應,讀取響應體中的二進位數據(讀文件的操作)
- 將讀取的文件二進位數據寫入到磁碟中
代碼實現
現在已經瞭解了原理,那麼就開始代碼實現吧!
namespace WebDownLoad
{
// 一個下載任務類
public class DownLoadTask
{
public async Task Start(string url,string targetUrl)
{
try
{
// 1.先創建一個HttpClient連接
// 由於HttpClient實現了IDispose介面,所以我們可以回收它的資源
using HttpClient client = new HttpClient();
// 1.1.某些網站會反爬,所以我們需要設置一些參數
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36 Edg/118.0.2088.61");
// 2.進行非同步請求
HttpResponseMessage response = await client.GetAsync(url);
// 3.請求成功
if (response.IsSuccessStatusCode)
{
// 3.1.讀取文件的類型
string? type = response.Content.Headers.ContentType?.MediaType;
// 3.2.文件的總大小
long? totalSize = response.Content.Headers.ContentLength;
// 3.3.獲取文件流
// 緩存區的大小為 10 kb
// 當前下載的大小
long downloadSize = 0;
int length = 0;
using BufferedStream bufferedStream = new BufferedStream(
await response.Content.ReadAsStreamAsync());
// 緩衝區大小 0.5KB
byte[] bufferSize = new byte[1024];
IEnumerable<byte> targetBuffer = new List<byte>();
string suffix = GetType(type);
while ((length = bufferedStream.Read(bufferSize, 0, bufferSize.Length)) != 0)
{
downloadSize += length;
long? progress = downloadSize * 100 / totalSize ;
targetBuffer = targetBuffer.Concat(bufferSize.ToList());
await Console.Out.WriteAsync($"\r下載中{progress}%");
}
await Console.Out.WriteLineAsync();
File.WriteAllBytes(targetUrl + Random.Shared.Next(10, 10000) + suffix, targetBuffer.ToArray());
}
// 4.請求失敗
else
{
Console.WriteLine("請求下載失敗");
}
}catch (HttpRequestException e)
{
Console.WriteLine($"請求下載失敗:{e.Message}");
}
}
// 檢測文件的類型
private string GetType(string type) {
string suffix = "";
if (type.Contains("jpeg"))
{
suffix = ".jpg";
}
else if (type.Contains("application/octet-stream"))
{
suffix = ".exe";
}
else if (type.Contains("png"))
{
suffix = ".png";
}
else if (type.Contains("mp4"))
{
suffix = ".mp4";
}
else if (type.Contains("avi"))
{
suffix = ".avi";
}
else if (type.Contains("mp3"))
{
suffix = ".mp3";
}
else if (type.Contains("mpeg"))
{
suffix = ".m4a";
}
return suffix;
}
}
}
我上面的這段代碼,其實有點累贅,大家可以寫的更好,不必看我的寫法,我對C#的很多類不太熟悉,所以,整體代碼的缺點還是很多的。
這裡其實為了做出一個正在下載的效果,讓控制台用戶有體驗,做了很多不必要的操作
這裡為了使得可以一次性下載多個文件使用非同步的操作,來提升程式的下載接收量,其實大家也可以不用非同步操作,使用線程來實現
最後實驗一下
using WebDownLoad;
namespace WebDownLoad
{
public class Program
{
public async static Task Main(string[] args) {
while (true)
{
Console.WriteLine("請輸入下載地址(如果輸入0退出):");
string url = Console.ReadLine();
if ("0".Equals(url))
{
break;
}
DownLoadTask task = new DownLoadTask();
task.Start(url, "E:\\網路下載\\");
}
}
}
}
當前的文件夾中是沒有東西的
這裡還可以繼續下載,同時還有一個問題,我們需要瞭解,就是在C#中,其實非同步並不會新開一個線程,C#底層實現非同步其實本質上是使用switch goto 來進狀態跳轉,也就是它並不會實際上加快處理速度,但是可以加大程式的接收速度,也就是接收很快,但是處理不變,要加快處理還是得開線程,我們線程本身就是帶有非同步性的,所以這個程式使用線程實現可能是更好的