C#斷點續傳下載文件


知識點:

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

2合並文件時,每個文件都有結束符“\0”。如:當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);

                    //由於一個文件拆分成多個文件時,每個文件最后都會拼接上結尾符"\0",導致總長度多出(n-1)個字符,需要需要針對前面(n-1)個文件去除最后的"\0"。
                    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

    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM