C#实现http多线程断点续传下载文件

原创 2016年05月31日 22:21:05
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Net;
using u8 = System.Byte;
using u16 = System.UInt16;
using s32 = System.Int32;
using u32 = System.UInt32;
using f32 = System.Single;


namespace ConsoleApplication1
{
    public class HttpDownloadFile
    {
        public void SetURL(string URL)
        {
            mURL = URL;
        }

        //单线程下载文件大小阀值
        public void SetSingleThFileSize(u32 FileSize)
        {
            mSingleThFileSize = FileSize;
        }

        //下载速度KB/S,需要外部循环调用Run函数更新这个值
        public f32 GetDownloadSpeed()
        {
            return mDownloadSpeed / 1024;
        }

        //添加异步下载文件,这里提供输入文件大小的原因是
        //方便灵活扩展,比如游戏更新,我们有可能先同步下载一个version.txt的
        //文件里面包含类似md5和文件大小与本地比较找到需要下载的文件
        //这时我们已经可以从version.txt里面提前知道需要下载文件的大小了
        public void AddAsyncDownloadFile(string RemoteFilePath, string LocalFilePath, u32 RemoteFileSize = 0)
        {
            mDownloadFiles.Add(new DownloadFile(RemoteFilePath, LocalFilePath, RemoteFileSize));
        }

        //同步下载文件
        public bool SyncDownloadFile(string RemoteFilePath, string LocalFilePath)
        {
            try
            {
                var HttpRequest = WebRequest.Create(mURL + "//" + RemoteFilePath) as HttpWebRequest;
                var HttpResponse = HttpRequest.GetResponse() as HttpWebResponse;
                var HttpStream = HttpResponse.GetResponseStream();
                _PrepareDirForFile(LocalFilePath);
                var OutStream = new FileStream(LocalFilePath, FileMode.Create);
                var Buffer = new Byte[1024];
                var ReadBytes = 0;
                while (true)
                {
                    ReadBytes = HttpStream.Read(Buffer, 0, Buffer.Length);
                    if (ReadBytes <= 0)
                    {
                        break;
                    }
                    OutStream.Write(Buffer, 0, ReadBytes);
                }
                OutStream.Close();
                HttpStream.Close();
                HttpResponse.Close();
                HttpRequest.Abort();
                return true;
            }
            catch (WebException e)
            {
                Console.WriteLine(e.Message);
                return false;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return false;
            }
        }

        public void StartAsyncDownloadFiles()
        {
            _CutFile();
            Thread t;
            for (s32 i = 0; i < Environment.ProcessorCount; ++i)
            {
                t = new Thread(new ParameterizedThreadStart(_AsyncDownloadFiles));
                t.Start(i);
            }

            //文件下载进度、总下载进度、下载完成事件请自行添加了...
        }

        void Run(f32 TimeDelta)
        {
            if (mDownloadBytes >= mTotalDownloadBytes)
            {
                return;
            }
            mTimeDelta += TimeDelta;
            if (mTimeDelta >= 1.0f)
            {
                mDownloadSpeed = mDownloadBytesCounter;
                mTimeDelta = .0f;
                mDownloadBytesCounter = 0;
            }
        }

        //获取文件大小,并且分割文件或者临时文件
        void _CutFile()
        {
            HttpWebRequest HttpRequest;

            HttpWebResponse HttpResponse;

            DownloadFile MyFile;

            u32 Block = 0;

            u32 Mod = 0;

            FileStream InStream;

            string LocalTempFilePath = String.Empty;

            for (s32 i = 0; i < mDownloadFiles.Count; ++i)
            {
                MyFile = mDownloadFiles[i];

                //得到文件大小
                if (MyFile.mRemoteFileSize <= 0)
                {
                    try
                    {
                        HttpRequest = WebRequest.Create(mURL + "//" + MyFile.mRemoteFilePath) as HttpWebRequest;
                        HttpResponse = HttpRequest.GetResponse() as HttpWebResponse;
                        MyFile.mRemoteFileSize = (u32)HttpResponse.ContentLength;
                        HttpResponse.Close();
                        HttpRequest.Abort();
                    }
                    catch (WebException e)
                    {
                        Console.WriteLine(e.Message);
                        continue;
                    }
                }

                //检测是否有未下载完毕的临时文件
                for (s32 j = 0; j < Environment.ProcessorCount; ++j)
                {
                    if (File.Exists(MyFile.mLocalFilePath + j.ToString()))
                    {
                        LocalTempFilePath = MyFile.mLocalFilePath + j.ToString();
                        break;
                    }
                }

                //文件块大小
                Block = MyFile.mRemoteFileSize / (u32)Environment.ProcessorCount;

                //余数
                Mod = MyFile.mRemoteFileSize % (u32)Environment.ProcessorCount;

                if (LocalTempFilePath.Length > 0)
                {
                    if (MyFile.mRemoteFileSize < mSingleThFileSize)
                    {
                        InStream = new FileStream(LocalTempFilePath, FileMode.Open);
                        MyFile.mDownloadBytes += (u32)InStream.Length;
                        MyFile.mFileBlocks.Enqueue(new DownloadFile.FileBlock((u32)InStream.Length, MyFile.mRemoteFileSize - 1, LocalTempFilePath));
                        InStream.Close();
                    }
                    else
                    {
                        DownloadFile.FileBlock MyFileBlock;
                        for (u32 j = 0; j < Environment.ProcessorCount; ++j)
                        {
                            LocalTempFilePath = MyFile.mLocalFilePath + j.ToString();
                            if (File.Exists(LocalTempFilePath))
                            {
                                InStream = new FileStream(LocalTempFilePath, FileMode.Open);

                                MyFile.mDownloadBytes += (u32)InStream.Length;
                                MyFileBlock = new DownloadFile.FileBlock(j * Block + (u32)InStream.Length, (j + 1) * Block - 1, LocalTempFilePath);

                                if (j == Environment.ProcessorCount - 1)
                                {
                                    MyFileBlock.mEndBlock += Mod;
                                }
                                MyFile.mFileBlocks.Enqueue(MyFileBlock);
                                InStream.Close();
                            }
                            else
                            {
                                MyFileBlock = new DownloadFile.FileBlock(j * Block, (j + 1) * Block - 1, LocalTempFilePath);
                                if (j == Environment.ProcessorCount - 1)
                                {
                                    MyFileBlock.mEndBlock += Mod;
                                }
                                MyFile.mFileBlocks.Enqueue(MyFileBlock);
                            }
                        }
                    }
                    LocalTempFilePath = String.Empty;
                }
                else
                {
                    if (MyFile.mRemoteFileSize < mSingleThFileSize)
                    {
                        MyFile.mFileBlocks.Enqueue(new DownloadFile.FileBlock(0, MyFile.mRemoteFileSize - 1));
                    }
                    else
                    {
                        DownloadFile.FileBlock MyBlock;
                        for (u32 j = 0; j < Environment.ProcessorCount; ++j)
                        {
                            MyBlock = new DownloadFile.FileBlock(j * Block, (j + 1) * Block - 1);
                            if (j == Environment.ProcessorCount - 1)
                            {
                                MyBlock.mEndBlock += Mod;
                            }
                            MyFile.mFileBlocks.Enqueue(MyBlock);
                        }
                    }
                }
                mDownloadBytes += MyFile.mDownloadBytes;
                mTotalDownloadBytes += MyFile.mRemoteFileSize - MyFile.mDownloadBytes;
            }
        }

        private void _AsyncDownloadFiles(System.Object o)
        {
            var ThreadID = (u16)o;

            HttpWebRequest HttpRequest;

            HttpWebResponse HttpResponse;

            Stream HttpStream;

            FileStream OutStream;

            s32 ReadBytes = 0;

            Byte[] Buffer = new Byte[1024];

            DownloadFile FileQueue;
            DownloadFile.FileBlock BlockQueue;
            while (true)
            {
                lock (this)
                {
                    if (mDownloadFiles.Count <= 0)
                    {
                        break;
                    }
                    FileQueue = mDownloadFiles[0];
                    BlockQueue = FileQueue.mFileBlocks.Dequeue();
                }

                try
                {
                    //临时文件有可能恰好下完,这里就不用在请求了
                    if (BlockQueue.mBeginBlock > BlockQueue.mEndBlock)
                    {
                        goto SKIP_REQUEST;
                    }
                    HttpRequest = WebRequest.Create(mURL + "//" + FileQueue.mRemoteFilePath) as HttpWebRequest;
                    HttpRequest.AddRange(BlockQueue.mBeginBlock, BlockQueue.mEndBlock);
                    HttpResponse = HttpRequest.GetResponse() as HttpWebResponse;
                    HttpStream = HttpResponse.GetResponseStream();

                    //下载全新的临时文件
                    if ("" == BlockQueue.mTempFilePath)
                    {
                        _PrepareDirForFile(FileQueue.mLocalFilePath + ThreadID.ToString());
                        OutStream = new FileStream(FileQueue.mLocalFilePath + ThreadID.ToString(), FileMode.Create);
                    }
                    //继续下载未下载完的临时文件
                    else
                    {
                        OutStream = new FileStream(BlockQueue.mTempFilePath, FileMode.Append);
                    }

                    while (true)
                    {
                        ReadBytes = HttpStream.Read(Buffer, 0, Buffer.Length);
                        if (ReadBytes <= 0)
                        {
                            break;
                        }
                        OutStream.Write(Buffer, 0, ReadBytes);
                        lock (this)
                        {
                            FileQueue.mDownloadBytes += (u32)ReadBytes;
                            mDownloadBytesCounter = mDownloadBytes += (u32)ReadBytes;

                            f32 FilePercent = (f32)FileQueue.mDownloadBytes / FileQueue.mRemoteFileSize;//事件调用文件进度
                            //onDownloadFilePercent(FilePercent * 100, FileQueue.mRemoteFilePath);

                            f32 TotalPercent = (f32)mDownloadBytes / mTotalDownloadBytes;//事件调用总进度
                            //onDownloadPercent(TotalPercent * 100);
                        }
                    }
                    OutStream.Close();
                    HttpStream.Close();
                    HttpResponse.Close();
                    HttpRequest.Abort();
                    SKIP_REQUEST:
                    lock (this)
                    {
                        //一个文件所有块下载完毕,合并文件
                        if (FileQueue.mFileBlocks.Count <= 0)
                        {
                            _MergeFile(FileQueue, BlockQueue, ThreadID);
                            mDownloadFiles.RemoveAt(0);
                        }
                    }
                }
                catch (WebException e)
                {
                    Console.WriteLine(e.Message);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }
        }

        private void _MergeFile(DownloadFile MyFile, DownloadFile.FileBlock Block, u16 ThreadID)
        {
            if (MyFile.mRemoteFileSize < mSingleThFileSize)
            {
                if (File.Exists(MyFile.mLocalFilePath))
                {
                    File.Delete(MyFile.mLocalFilePath);
                }
                if ("" == Block.mTempFilePath)
                {
                    File.Move(MyFile.mLocalFilePath + ThreadID.ToString(), MyFile.mLocalFilePath);
                }
                else
                {
                    File.Move(Block.mTempFilePath, MyFile.mLocalFilePath);
                }
                
                return;
            }
            FileStream OutStream = new FileStream(MyFile.mLocalFilePath, FileMode.Create);
            FileStream InStream;
            s32 ReadBytes = 0;
            Byte[] Buffer = new Byte[1024];
            for (u32 i = 0; i < Environment.ProcessorCount; ++i)
            {
                InStream = new FileStream(MyFile.mLocalFilePath + i.ToString(), FileMode.Open);
                while (true)
                {
                    ReadBytes = InStream.Read(Buffer, 0, Buffer.Length);
                    if (ReadBytes <= 0)
                    {
                        break;
                    }
                    OutStream.Write(Buffer, 0, ReadBytes);
                }
                InStream.Close();
                File.Delete(MyFile.mLocalFilePath + i.ToString());
            }
            OutStream.Close();
        }

        private void _PrepareDirForFile(string FilePath)
        {
            var Dir = Path.GetDirectoryName(FilePath);
            if (false == Directory.Exists(Dir))
            {
                Directory.CreateDirectory(Dir);
            }
        }

        class DownloadFile
        {
            public DownloadFile(string RemoteFilePath, string LocalFilePath, u32 RemoteFileSize = 0)
            {
                mRemoteFilePath = RemoteFilePath;
                mLocalFilePath = LocalFilePath;
                mRemoteFileSize = RemoteFileSize;
                mDownloadBytes = 0;
            }
            public string mRemoteFilePath;
            public string mLocalFilePath;
            public u32 mRemoteFileSize;
            public u32 mDownloadBytes;
            public class FileBlock
            {
                public FileBlock(u32 BeginBlock, u32 EndBlock, string TempFilePath = "")
                {
                    mBeginBlock = BeginBlock;
                    mEndBlock = EndBlock;
                    mTempFilePath = TempFilePath;
                }
                public u32 mBeginBlock;
                public u32 mEndBlock;
                public string mTempFilePath;
            }
            public Queue<FileBlock> mFileBlocks = new Queue<FileBlock>();
        }
        List<DownloadFile> mDownloadFiles = new List<DownloadFile>();

        string mURL = "http://127.0.0.1:8080//download";

        u32 mSingleThFileSize =1024 * 1024 * 10;

        f32 mDownloadSpeed = .0f;

        f32 mTimeDelta = .0f;

        u32 mDownloadBytesCounter = 0;

        u32 mDownloadBytes = 0;

        u32 mTotalDownloadBytes = 0;
    }

    class Program
    {
        static void Main(string[] args)
        {
            //代码写的不太完整,但是核心部分和流程已经可以作为参考(临时写的只是编译通过,未做测试)
            HttpDownloadFile Download = new HttpDownloadFile();
            Download.SetURL( "http://127.0.0.1:8080//download" );
            Download.SetSingleThFileSize( 1024 * 1024 * 10 );
            Download.AddAsyncDownloadFile("1.rmvb", "download//1.rmvb");
            Download.AddAsyncDownloadFile("2.rmvb", "download//2.rmvb");
            Download.StartAsyncDownloadFiles();
            Thread.Sleep(100000);
        }
    }
}

相关文章推荐

C#多线程开发6:使用lock语句同步多个线程

在多个线程之间共享数据时,需要考虑线程同步问题,必须确保每次只有一个线程访问和改变共享数据。 C#中使用lock语句可以轻松地设置和解除锁定以期达到每次只有一个线程访问和改变共享数据的目的。 下面...
  • tiana0
  • tiana0
  • 2015年05月25日 18:10
  • 3508

分享一个常用的sqlHelper类

using System; using System.Collections.Generic; using System.Text; using System.Configuration; u...

Asp.net(c#)实现多线程断点续传下载大文件

/// /// 下载的方法 /// /// 下载文件的路径 public void Reget(string filepath) ...

Http/FTP多线程断点续传下载组件

  • 2012年05月18日 15:50
  • 604KB
  • 下载

http协议 文件下载原理及多线程断点续传

最近研究了一下关于文件下载的相关内容,觉得还是写些东西记下来比较好。起初只是想研究研究,但后来发现写个可重用性比较高的模块还是很有必要的,我想这也是大多数开发人员的习惯吧。 对于HTTP协议,向服务...
  • zhuhuiby
  • zhuhuiby
  • 2011年08月28日 05:19
  • 20074

QT HTTP断点续传下载(非多线程)

  • 2012年04月21日 09:24
  • 1.34MB
  • 下载

Android下使用Http协议实现多线程断点续传下载

0.使用多线程下载会提升文件下载的速度,那么多线程下载文件的过程是: (1)首先获得下载文件的长度,然后设置本地文件的长度     HttpURLConnection.getContentLeng...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C#实现http多线程断点续传下载文件
举报原因:
原因补充:

(最多只允许输入30个字)