zoukankan      html  css  js  c++  java
  • C#断点续传下载文件

    知识点:

    1分段下载:httprequest.AddRange(begin, end);

    2合并文件时,每个文件都有结束符“”。如:当1个文件下载为2个文件时,按顺序合并文件需要将第一个文件的结束符去掉,防止文件合并后的新文件与原始文件不一致。

     源码下载:

    https://files.cnblogs.com/files/a735882640/20180825DownTest.zip

    关键代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;
    using System.Threading;
    using System.Net;
    
    namespace DownTest
    {
        /// <summary>
        /// 下载文件类
        /// </summary>
        public class MultiDownload
        {
            #region 变量
    
            private int _threadNum;    //线程数量
            private long _fileSize;    //文件大小
            private string _fileUrl;   //文件地址
            private string _fileName;   //文件名
            private string _saverootpath = "";
            private string _savePath;   //保存路径
            private short _threadCompleteNum; //线程完成数量
            private bool _isComplete;   //是否完成
            private volatile int _downloadSize; //当前下载大小(实时的)
            private Thread[] _thread;   //线程数组
            private List<string> _tempFiles = new List<string>();
            private object locker = new object();
    
            #endregion
    
            #region 事件
            public delegate void ReadHandler(DownStatus status, long size, long currentsize, Exception e);
            public event ReadHandler Reading;
    
            #endregion
    
            #region 属性
            /// <summary>
            /// 下载状态
            /// </summary>
            public DownStatus DownStatus { get; set; }
            /// <summary>
            /// 保存路径
            /// </summary>
            public string SavePath { get { return _savePath; } }
    
            #endregion
    
            #region 构造函数
    
            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="threahNum"></param>
            /// <param name="fileUrl"></param>
            /// <param name="saverootpath"></param>
    
            public MultiDownload(int threahNum, string fileUrl, string saverootpath)
            {
                var savePath = saverootpath + "\" + System.IO.Path.GetFileName(fileUrl);
    
                this._saverootpath = saverootpath;
                this._threadNum = threahNum;
                this._thread = new Thread[threahNum];
                this._fileUrl = fileUrl;
                this._savePath = savePath;
                this.DownStatus = DownStatus.None;
            }
    
            #endregion
    
            #region 检查是否有效
            /// <summary>
            /// 检查是否有效
            /// </summary>
            /// <param name="msg"></param>
            /// <returns></returns>
            public bool IsValid(out string msg)
            {
                msg = "";
                var issuccess = true;
                try
                {
                    if (string.IsNullOrEmpty(this._fileUrl))
                    {
                        throw new Exception("请输入url地址!");
                    }
                    if (string.IsNullOrEmpty(this._savePath))
                    {
                        throw new Exception("请输入保存地址!");
                    }
                    if (File.Exists(this._savePath))
                    {
                        throw new Exception(string.Format("保存文件[{0}]已存在!", this._savePath));
                    }
                    if (Directory.Exists(this._saverootpath) == false)
                    {
                        Directory.CreateDirectory(this._saverootpath);
                    }
                }
                catch (Exception ex)
                {
                    msg = ex.Message;
                    issuccess = false;
                }
                return issuccess;
            }
            #endregion
    
            #region 开始任务
    
            /// <summary>
            /// 开始任务
            /// </summary>
            public void Start()
            {
                try
                {
                    var msg = "";
                    var isvalid = IsValid(out msg);
                    if (isvalid == false)
                    {
                        throw new Exception(msg);
                    }
    
                    //初始化全局变量
                    _threadCompleteNum = 0;
                    _tempFiles = new List<string>();
    
    
                    this.DownStatus = DownStatus.Downing;
                    var readthread = new Thread(new ParameterizedThreadStart(ReadingThread));
                    readthread.Start();
    
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(_fileUrl);
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    _fileSize = response.ContentLength;
                    Console.WriteLine("_fileSize =" + _fileSize);
                    int singelNum = (int)(_fileSize / _threadNum);  //平均分配
                    int remainder = (int)(_fileSize % _threadNum);  //获取剩余的
                    request.Abort();
                    response.Close();
                    for (int i = 0; i < _threadNum; i++)
                    {
                        var begin = singelNum * i;
                        var end = singelNum * (i + 1);
    
                        //最后一个进程,需要将剩余的也下载
                        if ((i + 1) == _threadNum)
                        {
                            end += remainder - 1;
                        }
                        //下载指定位置的数据
                        int[] ran = new int[] { begin, end };
                        _thread[i] = new Thread(new ParameterizedThreadStart(Download));
                        _thread[i].Name = System.IO.Path.GetFileNameWithoutExtension(_fileUrl) + "_{0}".Replace("{0}", Convert.ToString(i + 1));
                        _thread[i].Start(ran);
                    }
                }
                catch (Exception e)
                {
                    this.DownStatus = DownStatus.Stop;
                }
            }
    
            /// <summary>
            /// 检查下载进度进程
            /// </summary>
            /// <param name="obj"></param>
            private void ReadingThread(object obj)
            {
                try
                {
                    while (true)
                    {
                        try
                        {
                            long currentsize = 0;
                            foreach (var file in _tempFiles)
                            {
                                if (File.Exists(file))
                                {
                                    FileInfo fileInfo = new FileInfo(file);
                                    currentsize += fileInfo.Length;
                                }
                            }
                            //读取事件
                            if (Reading != null)
                            {
                                Reading(this.DownStatus, _fileSize, currentsize, null);
                            }
                            //结束进度进程
                            if (this.DownStatus != DownStatus.Downing)
                            {
                                break;
                            }
    
                            Thread.Sleep(1000);
    
                        }
                        catch (Exception ex)
                        {
                        }
                    }
                }
                catch (Exception ex)
                {
                }
            }
    
            /// <summary>
            /// 下载文件进程
            /// </summary>
            /// <param name="obj"></param>
            private void Download(object obj)
            {
                try
                {
                    Stream httpFileStream = null, localFileStram = null;
                    try
                    {
                        int[] ran = obj as int[];
    
                        var begin = ran[0];
                        var end = ran[1];
    
                        string tmpFileBlock = GetTmpPath() + Thread.CurrentThread.Name + ".tmp";
                        lock (locker)
                        {
                            //添加临时文件列表
                            if (_tempFiles.Contains(tmpFileBlock) == false)
                            {
                                _tempFiles.Add(tmpFileBlock);
                            }
                        }
    
                        var seek = 0;
                        var isneeddown = true;
    
                        Console.WriteLine("tmpFileBlock=" + tmpFileBlock + "begin=" + begin + ",end=" + end);
    
                        //如果文件已存在,则获取已下载长度,继续下载
                        if (File.Exists(tmpFileBlock))
                        {
                            FileInfo fileInfo = new FileInfo(tmpFileBlock);
                            var existlength = (int)fileInfo.Length;
    
                            var needsize = end - begin + 1;
    
                            Console.WriteLine("existlength=" + existlength + ",needsize=" + needsize + ",end=" + end + ",begin=" + begin);
    
                            if (existlength > needsize)
                            {
                                //文件长度超过需要下载的长度,表示文件不是该任务创建的,需要删除,重新下载                             
                                File.Delete(tmpFileBlock);
    
                                seek = 0;
                                isneeddown = true;
                            }
                            else if (existlength == needsize)
                            {
                                //文件下载已完成
                                isneeddown = false;
                            }
                            else
                            {
                                //文件尚未下载完成,指定下载位置
    
                                begin = (existlength - 1);//已下载的长度-1(位置从0开始)
                                seek = existlength;//已下载的长度
                                isneeddown = true;
    
                            }
                        }
                        Console.WriteLine("isneeddown=" + isneeddown + "begin=" + begin + ",end=" + end + ",seek=" + seek);
    
                        //判断是否需要下载数据
                        if (isneeddown)
                        {
                            HttpWebRequest httprequest = (HttpWebRequest)WebRequest.Create(_fileUrl);
                            Console.WriteLine("begin =" + begin + ",end=" + end);
                            Console.WriteLine("seek=" + seek);
                            httprequest.AddRange(begin, end);
                            HttpWebResponse httpresponse = (HttpWebResponse)httprequest.GetResponse();
                            httpFileStream = httpresponse.GetResponseStream();
    
                            //如果不存在,则新建
                            localFileStram = new FileStream(tmpFileBlock, FileMode.OpenOrCreate);
                            //指定写入位置
                            localFileStram.Seek(seek, SeekOrigin.Begin);
                            byte[] by = new byte[5000];
                            int getByteSize = httpFileStream.Read(by, 0, (int)by.Length); //Read方法将返回读入by变量中的总字节数
    
                            while (getByteSize > 0)
                            {
                                if (this.DownStatus == DownStatus.Stop)
                                {
                                    throw new Exception("任务已停止!");
                                }
                                Thread.Sleep(20);
                                lock (locker) _downloadSize += getByteSize;
                                localFileStram.Write(by, 0, getByteSize);
                                getByteSize = httpFileStream.Read(by, 0, (int)by.Length);
                            }
                        }
                        lock (locker) _threadCompleteNum++;
                    }
                    catch (Exception ex)
                    {
                        throw new Exception(ex.Message.ToString());
                    }
                    finally
                    {
                        if (httpFileStream != null) httpFileStream.Dispose();
                        if (localFileStram != null) localFileStram.Dispose();
                    }
                    if (_threadCompleteNum == _threadNum)
                    {
                        Complete();
                        _isComplete = true;
                        this.DownStatus = DownStatus.Complete;
                    }
                }
                catch (Exception e)
                {
                    this.DownStatus = DownStatus.Stop;
                }
            }
    
            /// <summary>
            /// 下载完成后合并文件块
            /// </summary>
            private void Complete()
            {
                Stream mergeFile = new FileStream(@_savePath, FileMode.Create);
                BinaryWriter AddWriter = new BinaryWriter(mergeFile);
                //按序号排序
                _tempFiles.Sort();
                int i = 0;
                foreach (string file in _tempFiles)
                {
                    i++;
                    using (FileStream fs = new FileStream(file, FileMode.Open))
                    {
                        BinaryReader TempReader = new BinaryReader(fs);
    
                        //由于一个文件拆分成多个文件时,每个文件最后都会拼接上结尾符"",导致总长度多出(n-1)个字符,需要需要针对前面(n-1)个文件去除最后的""。
                        if (i == _tempFiles.Count)
                        {
                            AddWriter.Write(TempReader.ReadBytes((int)fs.Length));
                        }
                        else
                        {
                            AddWriter.Write(TempReader.ReadBytes((int)fs.Length - 1));
                        }
                        TempReader.Close();
                    }
                }
                AddWriter.Close();
                //删除临时文件夹
                foreach (string file in _tempFiles)
                {
                    //File.Delete(file);
                }
            }
            #endregion
    
            #region 删除临时文件
    
            /// <summary>
            /// 删除临时文件
            /// </summary>
            public void DeleteTmpFile()
            {
                this.DownStatus = DownStatus.Deleted;
                foreach (string file in _tempFiles)
                {
                    File.Delete(file);
                }
            }
    
            #endregion
    
            #region 暂停任务
    
            /// <summary>
            /// 暂停
            /// </summary>
            public void Stop()
            {
                this.DownStatus = DownStatus.Stop;
            }
    
            #endregion
    
            #region 私有方法 
    
            /// <summary>
            /// 获取临时文件夹
            /// </summary>
            /// <returns></returns>
            private string GetTmpPath()
            {
                return System.IO.Path.GetTempPath();
            }
    
            #endregion
        }
    
        public enum DownStatus
        {
            None = 0,
            Downing = 1,
            Stop = 2,
            Complete = 3,
            Deleted = 4
    
        }
    }
  • 相关阅读:
    JavaScript实现HTML导航栏下拉菜单[悬浮显示]
    Paper Pal:一个中英文论文及其代码大数据搜索平台
    小程序定位地图模块全系列开发教学(超详细)
    简单的个人介绍网页主页面【附代码】
    前端分页功能(通用)
    打造完美写作系统:Gitbook+Github Pages+Github Actions
    移动端布局
    三剑客var,let,const
    包含多个段的程序01 零基础入门学习汇编语言29
    更灵活的定位内存地址的方法01 零基础入门学习汇编语言32
  • 原文地址:https://www.cnblogs.com/a735882640/p/9639831.html
Copyright © 2011-2022 走看看