你的位置:首页 > ASP.net教程

[ASP.net教程]基于HTTP的断点传输


最近项目需要做一个类似于迅雷的文件下载功能,这类需求可能比较常见,希望可以帮助到有需要的同学

要求:

1.支持断点传输

2. 多文件同时下载

3. 由于是客户内部试用,服务器只支持HTTP文件下载不支持FTP文件(并没有用户权限的要求)

 

由于根据服务器传来的Json字符串解析后进行下载,首先构建Json对象,推荐一个库Newtonsoft.Json

 

 public class DownloadFile  {    [JsonProperty("police_num")]    public string PoliceNumber { get; set; }    [JsonProperty("equipment_num")]    public string EquipmentNumber { get; set; }    [JsonProperty("upload_date")]    public DateTime UploadDateTime { get; set; }    [JsonProperty("type")]    public string FileType { get; set; }    [JsonProperty("file_name")]    public string FileName { get; set; }    [JsonProperty("file_path")]    public string FilePath { get; set; }  }

JsonProperty不区分大小写

public class DownloadTask : ObservableObject  {    /// <summary>    /// </summary>    public static readonly int BufferSize = 1024;    /// <summary>    ///   一次请求大小,每次请求100kb    /// </summary>    public static long Step = 1024*100;    private int _process;    /// <summary>    ///   下载文件参数    /// </summary>    public DownloadFile DownloadFile { get; set; }    /// <summary>    ///   下载文件状态    /// </summary>    public SegmentState SegmentState { get; set; }    /// <summary>    ///   默认存储路径    /// </summary>    public string DefaultDirectory { get; set; }    /// <summary>    ///   当前进度    /// </summary>    public long CurrentSize { get; set; }    /// <summary>    ///   文件流对象,用于生成文件    /// </summary>    public FileStream FileStream { get; set; }    /// <summary>    ///   下载起始位置    /// </summary>    public long RangeFrom { get; set; }    /// <summary>    ///   总大小    /// </summary>    public long TotalSize { get; set; }    /// <summary>    ///   链接地址    /// </summary>    public string Url { get; set; }    /// <summary>    ///   界面显示进度    /// </summary>    public int Process    {      get { return _process; }      set      {        _process = value;        RaisePropertyChanged("Process");      }    }  }


下载服务类,DownloaderService.cs,功能包括启动下载,暂停,针对文件是否存在进行初始化,获取文件大小方便界面显示
 public class DownloaderService  {    public void OnStart(DownloadTask downloadTask)    {      if (!InitService(downloadTask)) return;      var downloadThread = new Thread(BeginDownloadFile) { IsBackground = true };      downloadThread.Start(downloadTask);    }    public void OnStop(DownloadTask downloadTask)    {      downloadTask.SegmentState = SegmentState.Paused;    }    private bool InitService(DownloadTask downloadTask)    {      try      {        if (string.IsNullOrEmpty(downloadTask.Url))        {          return false;        }        downloadTask.DownloadFile.FileName = downloadTask.Url.Substring(downloadTask.Url.LastIndexOf('/') + 1);        var fullName = Path.Combine(downloadTask.DefaultDirectory, downloadTask.DownloadFile.FileName);        if (File.Exists(fullName))        {          if (downloadTask.TotalSize == 0)          {            GetTotalSize(downloadTask);          }          if (downloadTask.CurrentSize == downloadTask.TotalSize)          {            if (MessageBox.Show("文件已经存在是否覆盖此文件", "TCL", MessageBoxButton.YesNo, MessageBoxImage.Information) !=            MessageBoxResult.Yes)              return false;            File.Delete(fullName);            downloadTask.CurrentSize = 0;          }          downloadTask.FileStream = new FileStream(fullName, FileMode.OpenOrCreate, FileAccess.ReadWrite);          downloadTask.SegmentState = SegmentState.Downloading;          return true;        }        downloadTask.SegmentState = SegmentState.Downloading;        downloadTask.FileStream = new FileStream(fullName, FileMode.OpenOrCreate, FileAccess.ReadWrite);        return true;      }      catch (Exception ex)      {        LogLightConsole.WriteLog(ex.ToString(), EunmMsgType.Error);        return false;      }    }    private void BeginDownloadFile(object obj)    {      try      {        var downloadTask = (DownloadTask)obj;        while (true)        {          if (downloadTask.SegmentState == SegmentState.Paused)          {            downloadTask.FileStream.Close();            break;          }          //从0计数,需要减一          var from = downloadTask.CurrentSize;          if (from < 0)          {            from = 0;          }          var to = downloadTask.CurrentSize + DownloadTask.Step - 1;          if (to >= downloadTask.TotalSize && downloadTask.TotalSize > 0)          {            to = downloadTask.TotalSize - 1;          }          if (downloadTask.TotalSize == 0)          {            GetTotalSize(downloadTask);          }          if (from >= downloadTask.TotalSize || downloadTask.CurrentSize >= downloadTask.TotalSize)          {            downloadTask.SegmentState = SegmentState.Finished;            downloadTask.FileStream.Close();            downloadTask.Process = 100;            return;          }          var request = (HttpWebRequest)WebRequest.Create(downloadTask.Url);          //request.Method = "GET";                request.AddRange("bytes", from, to);          request.Timeout = 1000 * 20;          var response = (HttpWebResponse)request.GetResponse();          var buffer = new byte[1024];          using (var stream = response.GetResponseStream())          {            if (stream != null)            {              var size = stream.Read(buffer, 0, buffer.Length);              while (size > 0)              {                //只将读出的字节写入文件                downloadTask.FileStream.Write(buffer, 0, size);                downloadTask.CurrentSize += size;                size = stream.Read(buffer, 0, buffer.Length);                downloadTask.Process = (int)(downloadTask.CurrentSize * 100 / downloadTask.TotalSize);                downloadTask.FileStream.Flush();              }            }            //如果返回的response头中Content-Range值为空,说明服务器不支持Range属性,不支持断点续传,返回的是所有数据            if (response.Headers["Content-Range"] == null)            {              downloadTask.SegmentState = SegmentState.Finished;            }          }        }      }      catch (Exception ex)      {        LogLightConsole.WriteLog("BeginDownloadFile" + ex, EunmMsgType.Error);      }    }    /// <summary>    ///   获取文件总大小    /// </summary>    public void GetTotalSize(DownloadTask downloadTask)    {      var request = (HttpWebRequest)WebRequest.Create(downloadTask.Url);      //request.Method = "POST";      //request.Headers.Add("charset:" + "utf-8");      request.Timeout = 1000 * 20;      var response = (HttpWebResponse)request.GetResponse();      downloadTask.TotalSize = response.ContentLength;    }  }

HTTP 1.1版本本身已经扩展了断点传输功能,但是这需要服务器同时也支持,这只是提供了一个简单的例子,具体的针对数据上下文以及成功失败的callback函数,就是做一下委托封装用就好,如果具体使用有什么问题可以私信我,谢谢大家支持!

response.Headers["Content-Range"] == null 说明服务器不支持断点传输