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

来源:互联网 发布:淘宝男装图片 编辑:程序博客网 时间:2024/04/29 17:06
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);        }    }}

0 0