C#异步下载文件

在C#当中,利用WebClient这个核心类,可以轻易的打造一个下载器。但是这里想要强调的是,我们用的是异步操作。所谓异步,是相对于同步的概念而言的。比如Web中的Ajax就是基于异步的。它能够提供良好的用户体验,让用户在进行操作时,不感觉到“卡”(不阻塞UI线程),能够同时进行其它的操作并能够随意的切换到任务界面。在下载文件时,如果文件过大,我们用同步的下载方式进行下载会感觉程序“假死”,其实程序在后台不断的运行,但我们看不到下载的过程。所以这时候使用异步方法能够有效的解决这个问题。
先看一下程序的界面:

实现上面的操作很简单,只需要几行代码就可以搞定。

private void button1_Click(object sender, EventArgs e)
{
  using (WebClient client = new WebClient())
  {
    client.DownloadFileAsync(new Uri(this.textBox1.Text.Trim()),Path.GetFileName(this.textBox1.Text.Trim()));
    client.DownloadProgressChanged += client_DownloadProgressChanged;
    client.DownloadFileCompleted += client_DownloadFileCompleted;
  }
} 

void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
  this.label1.Text = string.Format("当前接收到{0}字节,文件大小总共{1}字节", e.BytesReceived, e.TotalBytesToReceive);
  this.progressBar1.Value = e.ProgressPercentage;
} 

void client_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
  if (e.Cancelled)
  {
    MessageBox.Show("文件下载被取消", "提示", MessageBoxButtons.OKCancel);
  }
  this.progressBar1.Value = 0;
  MessageBox.Show("文件下载成功", "提示");
}

我们只需要在textbox中填入文件的地址,比如迅雷的下载地址,就可以用上面的代码进行下载了。
在C#当中,还可以利用HttpWebRequest进行文件的异步下载。下面的代码可能稍微有点复杂,但是可以帮助我们深入理解“异步“操作的过程。
我们先定义一个类,用于保存操作的状态:

/// <summary>
/// 请求状态
/// </summary>
public class RequestState
{
  /// <summary>
  /// 缓冲区大小
  /// </summary>
  public int BUFFER_SIZE { get; set; } 

  /// <summary>
  /// 缓冲区
  /// </summary>
  public byte[] BufferRead { get; set; } 

  /// <summary>
  /// 保存路径
  /// </summary>
  public string SavePath { get; set; } 

  /// <summary>
  /// 请求流
  /// </summary>
  public HttpWebRequest Request { get; set; } 

  /// <summary>
  /// 响应流
  /// </summary>
  public HttpWebResponse Response { get; set; } 

  /// <summary>
  /// 流对象
  /// </summary>
  public Stream ResponseStream { get; set; } 

  /// <summary>
  /// 文件流
  /// </summary>
  public FileStream FileStream { get; set; }
}

在一个Button的Click事件下,键入如下代码:

//下载文件的url
string url = this.textBox1.Text.Trim(); 

//创建一个初始化请求对象
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri(url)); 

//设置下载相关参数
RequestState requestState = new RequestState();
requestState.BUFFER_SIZE = 1024;
requestState.BufferRead = new byte[requestState.BUFFER_SIZE];
requestState.Request = request;
requestState.SavePath = Path.Combine("D:\\", Path.GetFileName(url));
requestState.FileStream = new FileStream(requestState.SavePath, FileMode.OpenOrCreate); 

//开始异步请求资源
request.BeginGetResponse(new AsyncCallback(ResponseCallback), requestState);

我们可以看到,异步的操作方法一般都是以Begin开头的BeginGetResponse,我们平时用的比较多的同步方法直接使用GetResponse。另外AsyncCallback是一个委托,前面讲过,它里面的参数是一个方法,我们起名为ResponseCallback,并且把requestState作为参数传递过去。
接下来就可以看一下ResponseCallback方法:

/// <summary>
/// 请求资源方法的回调函数
/// </summary>
/// <param name="asyncResult">用于在回调函数当中传递操作状态</param>
private void ResponseCallback(IAsyncResult asyncResult)
{
  RequestState requestState = (RequestState)asyncResult.AsyncState;
  requestState.Response = (HttpWebResponse)requestState.Request.EndGetResponse(asyncResult); 

  Stream responseStream = requestState.Response.GetResponseStream();
  requestState.ResponseStream = responseStream; 

  //开始异步读取流
  responseStream.BeginRead(requestState.BufferRead, 0, requestState.BufferRead.Length, ReadCallback, requestState);
}

我们可以看到,回调函数里面又有一个异步操作。它的任务是对响应流异步的读取到缓冲区当中。
再进一步,看一下ReadCallback回调函数。

/// <summary>
/// 异步读取流的回调函数
/// </summary>
/// <param name="asyncResult">用于在回调函数当中传递操作状态</param>
private void ReadCallback(IAsyncResult asyncResult)
{
  RequestState requestState = (RequestState)asyncResult.AsyncState;
  int read = requestState.ResponseStream.EndRead(asyncResult);
  if (read > 0)
  {
    //将缓冲区的数据写入该文件流
    requestState.FileStream.Write(requestState.BufferRead, 0, read); 

    //开始异步读取流
    requestState.ResponseStream.BeginRead(requestState.BufferRead, 0, requestState.BufferRead.Length, ReadCallback, requestState);
  }
  else
  {
    requestState.Response.Close();
    requestState.FileStream.Close();
  }
}

这里面是真正的将流写入文件的过程,并且用BeginRead方法递归的写入文件流直到文件完全写好为止。

以上就是本文的全部内容,希望对大家的学习有所帮助。

(0)

相关推荐

  • C#异步执行任务的方法

    本文实例讲述了C#异步执行任务的方法.分享给大家供大家参考.具体如下: // 异步执行耗时任务(适合不需要等它的执行结果的场景,如发邮件.发短信) Task.Factory.StartNew( () => { try { // 需要异步执行的操作比如发邮件.发短信等 SendEmail(...); } catch { //不做任何处理,防止线程异常导致程序崩溃 } } ); 希望本文所述对大家的C#程序设计有所帮助.

  • C#实现向多线程传参的三种方式实例分析

    本文实例讲述了C#实现向多线程传参的三种方式.分享给大家供大家参考,具体如下: 从<C#高级编程>了解到给线程传递参数有两种方式,一种方式是使用带ParameterizedThreadStart委托参数的Thread构造函数,另一种方式是创建一个自定义类,把线程的方法定义为实例的方法,这样就可以初始化实例的数据,之后启动线程. 方式一:使用ParameterizedThreadStart委托 如果使用了ParameterizedThreadStart委托,线程的入口必须有一个object类型的

  • C#实现线程池的简单示例

    本文以实例演示了C#线程池的简单实现方法.程序中定义了一个对象类,用以包装参数,实现多个参数的传递.成员属性包括两个输入参数和一个输出参数.代码简单易懂,备有注释便于理解. 具体实现代码如下: using System; using System.Threading; //定义对象类,用以包装参数,实现多个参数的传递 class Packet { //成员属性包括两个输入参数和一个输出参数 protected internal String inval1; protected internal

  • C#异步绑定数据实现方法

    本文实例讲述了C#异步绑定数据实现方法.分享给大家供大家参考.具体如下: using System; using System.Collections.Generic; using System.Text; using System.Data.SqlClient; using System.Data; using System.Windows.Forms; namespace WindowsApplication2 { public class AsyncCallBackOpeartion {

  • C#基于委托实现多线程之间操作的方法

    本文实例讲述了C#基于委托实现多线程之间操作的方法.分享给大家供大家参考,具体如下: 有的时候我们要起多个线程,更多的时候可能会有某个线程会去操作其他线程里的属性. 但是线程是并发的,一般的调用是无法实现我们的要求的. 于是,我们在这里就可以用委托,代码如下 private delegate void DelegateInfo(); private delegate void DelegateIsEnd(); //这个是线程调用其他线程的方法 private void Dowork() { //

  • C#多线程编程中的锁系统基本用法

    平常在多线程开发中,总避免不了线程同步.本篇就对net多线程中的锁系统做个简单描述. 目录 一:lock.Monitor      1:基础.      2: 作用域.      3:字符串锁.      4:monitor使用 二:mutex 三:Semaphore 四:总结 一:lock.Monitor 1:基础 Lock是Monitor语法糖简化写法.Lock在IL会生成Monitor. 复制代码 代码如下: //======Example 1=====             strin

  • 简单实现C#异步操作

    在.net4.0以后异步操作,并行计算变得异常简单,但是由于公司项目开发基于.net3.5所以无法用到4.0的并行计算以及Task等异步编程.因此,为了以后更方便的进行异步方式的开发,我封装实现了异步编程框架,通过BeginInvoke.EndInvoke的方式实现异步编程. 一.框架结构 整个框架包括四个部分 1.基类抽象Opeartor 我把每个异步执行过程称为一个Operate,因此需要一个Opeartor去执行 2.FuncAsync 异步的Func 3.ActionAsync 异步的A

  • C#线程池用法详细介绍

    介绍 .NET Framework提供了包含ThreadPool类的System.Threading 空间,这是一个可直接访问的静态类,该类对线程池是必不可少的.它是公共"线程池"设计样式的实现.对于后台运行许多各不相同的任务是有用的.对于单个的后台线种而言有更好的选项. 线程的最大数量.这是完全无须知道的.在.NET中ThreadPool的所有要点是它自己在内部管理线程池中线程.多核机器将比以往的机器有更多的线程.微软如此陈述"线程池通常有一个线程的最大数量,如果所有的线程

  • C#多线程编程之使用ReaderWriterLock类实现多用户读与单用户写同步的方法

    本文实例讲述了C#多线程编程之使用ReaderWriterLock类实现多用户读与单用户写同步的方法.分享给大家供大家参考,具体如下: 摘要:C#提供了System.Threading.ReaderWriterLock类以适应多用户读/单用户写的场景.该类可实现以下功能:如果资源未被写操作锁定,那么任何线程都可对该资源进行读操作锁定,并且对读操作锁数量没有限制,即多个线程可同时对该资源进行读操作锁定,以读取数据. 使用Monitor或Mutex进行同步控制的问题:由于独占访问模型不允许任何形式的

  • C#多线程学习之(四)使用线程池进行多线程的自动管理

    本文实例讲述了C#多线程学习之使用线程池进行多线程的自动管理.分享给大家供大家参考.具体如下: 在多线程的程序中,经常会出现两种情况: 一种情况:   应用程序中,线程把大部分的时间花费在等待状态,等待某个事件发生,然后才能给予响应 这一般使用ThreadPool(线程池)来解决: 另一种情况:线程平时都处于休眠状态,只是周期性地被唤醒 这一般使用Timer(定时器)来解决: ThreadPool类提供一个由系统维护的线程池(可以看作一个线程的容器),该容器需要 Windows 2000 以上系

  • 解析C#多线程编程中异步多线程的实现及线程池的使用

    0.线程的本质 线程不是一个计算机硬件的功能,而是操作系统提供的一种逻辑功能,线程本质上是进程中一段并发运行的代码,所以线程需要操作系统投入CPU资源来运行和调度. 1.多线程: 使用多个处理句柄同时对多个任务进行控制处理的一种技术.据博主的理解,多线程就是该应用的主线程任命其他多个线程去协助它完成需要的功能,并且主线程和协助线程是完全独立进行的.不知道这样说好不好理解,后面慢慢在使用中会有更加详细的讲解. 2.多线程的使用: (1)最简单.最原始的使用方法:Thread oGetArgThre

  • C#线程池操作方法

    本文实例讲述了C#线程池操作方法.分享给大家供大家参考.具体如下: static void Main(string[] args) { //设置线程池中的线程数最大为1000, //第一个为工作者线程,第二个为I/O线程 ThreadPool.SetMaxThreads(1000, 1000); for (int i = 0; i < 10;i ) { ThreadPool.QueueUserWorkItem(new WaitCallback(ShowMessage), string.Forma

随机推荐