using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading; namespace CsharpHttpHelper.Down { public class DownloadProgressListener : IDownloadProgressListener { public delegate void dlgSendMsg(DownMsg msg); public dlgSendMsg doSendMsg = null; private long presize= 0 ; public void OnDownloadSize(long size) { DownMsg msg = new DownMsg(); msg.speed = (float)(size - presize); //下载速度 msg.size = size; //下载总量 presize = size; msg.tag = 1; if (doSendMsg != null) doSendMsg(msg);//通知具体调用者下载进度 } } public class DownMsg { public int tag { get; set; } public long size { get; set; } public float speed { get; set; } } public class DownloadThread { private string saveFilePath; private string downUrl; private long block; private int threadId = -1; private long downLength; private bool finish = false; private FileDownloader downloader; public DownloadThread(FileDownloader downloader, string downUrl, string saveFile, long block, long downLength, int threadId) { this.downUrl = downUrl; this.saveFilePath = saveFile; this.block = block; this.downloader = downloader; this.threadId = threadId; this.downLength = downLength; } public void ThreadRun() { //task Thread td = new Thread(new ThreadStart(() => { if (downLength < block)//未下载完成 { try { int startPos = (int)(block * (threadId - 1) + downLength);//开始位置 int endPos = (int)(block * threadId - 1);//结束位置 Console.WriteLine("Thread " + this.threadId + " start download from position " + startPos + " and endwith " + endPos); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(downUrl); request.Referer = downUrl.ToString(); request.Method = "GET"; request.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; .NET CLR 2.0.1124)"; request.AllowAutoRedirect = false; request.ContentType = "application/octet-stream"; request.Accept = "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*"; request.Timeout = 10 * 1000; request.AllowAutoRedirect = true; request.AddRange(startPos, endPos); //Console.WriteLine(request.Headers.ToString()); //输出构建的http 表头 HttpWebResponse response = (HttpWebResponse)request.GetResponse(); WebResponse wb = request.GetResponse(); using (Stream _stream = wb.GetResponseStream()) { byte[] buffer = new byte[1024 * 50]; //缓冲区大小 long offset = -1; using (Stream threadfile = new FileStream(this.saveFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) //设置文件以共享方式读写,否则会出现当前文件被另一个文件使用. { threadfile.Seek(startPos, SeekOrigin.Begin); //移动文件位置 while ((offset = _stream.Read(buffer, 0, buffer.Length)) != 0) { //offset 实际下载流大小 downloader.append(offset); //更新已经下载当前总文件大小 threadfile.Write(buffer, 0, (int)offset); downLength += offset; //设置当前线程已下载位置 downloader.update(this.threadId, downLength); } threadfile.Close(); //using 用完后可以自动释放..手动释放一遍.木有问题的(其实是多余的) _stream.Close(); Console.WriteLine("Thread " + this.threadId + " download finish"); this.finish = true; } } } catch (Exception e) { this.downLength = -1; Console.WriteLine("Thread " + this.threadId + ":" + e.Message); } } })); td.IsBackground = true; td.Start(); } /// /// 下载是否完成 /// /// public bool isFinish() { return finish; } /// /// 已经下载的内容大小 /// /// 如果返回值为-1,代表下载失败 public long getDownLength() { return downLength; } } public class FileDownloader { /// /// 已下载文件长度 /// private long downloadSize = 0; /// /// 原始文件长度 /// private long fileSize = 0; /// /// 线程数 /// private DownloadThread[] threads; /// /// 本地保存文件 /// private string saveFile; /// /// 缓存各线程下载的长度 /// public Dictionary data = new Dictionary(); /// /// 每条线程下载的长度 /// private long block; /// /// 下载路径 /// private String downloadUrl; /// /// 获取线程数 /// /// 获取线程数 public int getThreadSize() { return threads.Length; } /// /// 获取文件大小 /// /// 获取文件大小 public long getFileSize() { return fileSize; } /// /// 累计已下载大小 /// /// 累计已下载大小 public void append(long size) { lock (this) //锁定同步..............线程开多了竟然没有同步起来.文件下载已经完毕了,下载总数量却不等于文件实际大小,找了半天原来这里错误的 { downloadSize += size; } } /// /// 更新指定线程最后下载的位置 /// /// threadId 线程id /// 最后下载的位置 public void update(int threadId, long pos) { if (data.ContainsKey(threadId)) { this.data[threadId] = pos; } else { this.data.Add(threadId, pos); } } /// /// 构建下载准备,获取文件大小 /// /// 下载路径 /// 文件保存目录 /// 下载线程数 public FileDownloader(string downloadUrl, string fileName, int threadNum) { try { //构建http 请求 this.downloadUrl = downloadUrl; // if (!Directory.Exists(fileSaveDir)) Directory.CreateDirectory(fileSaveDir); this.threads = new DownloadThread[threadNum]; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(downloadUrl); request.Referer = downloadUrl.ToString(); request.Method = "GET"; request.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; SV1; .NET CLR 2.0.1124)"; request.ContentType = "application/octet-stream"; request.Accept = "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*"; request.Timeout = 20 * 1000; request.AllowAutoRedirect = true; using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { if (response.StatusCode == HttpStatusCode.OK) { this.fileSize = response.ContentLength;//根据响应获取文件大小 if (this.fileSize <= 0) throw new Exception("获取文件大小失败"); string filename = Uri.UnescapeDataString(Path.GetFileName(downloadUrl));//获取文件名称 uri 解码中文字符 if (filename.Length == 0) throw new Exception("获取文件名失败"); this.saveFile = fileName; //构建保存文件 //计算每条线程下载的数据长度 this.block = (this.fileSize % this.threads.Length) == 0 ? this.fileSize / this.threads.Length : this.fileSize / this.threads.Length + 1; } else { throw new Exception("服务器返回状态失败,StatusCode:" + response.StatusCode); } } } catch (Exception e) { Console.WriteLine(e.Message); throw new Exception("无法连接下载地址"); } } /// /// 开始下载文件 /// /// 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null /// 已下载文件大小 public long download(IDownloadProgressListener listener) { try { using (FileStream fstream = new FileStream(this.saveFile, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite)) { if (this.fileSize > 0) fstream.SetLength(this.fileSize); fstream.Close(); } if (this.data.Count() != this.threads.Length) { this.data.Clear(); for (int i = 0; i < this.threads.Length; i++) { this.data.Add(i + 1, 0);//初始化每条线程已经下载的数据长度为0 } } for (int i = 0; i < this.threads.Length; i++) {//开启线程进行下载 long downLength = this.data[i + 1]; if (downLength < this.block && this.downloadSize < this.fileSize) {//判断线程是否已经完成下载,否则继续下载 + // Console.WriteLine("threads" + i.ToString() + ",下载块" + this.block.ToString() + " " + this.data[i + 1].ToString() + " " + downloadSize.ToString()); this.threads[i] = new DownloadThread(this, downloadUrl, this.saveFile, this.block, this.data[i + 1], i + 1); this.threads[i].ThreadRun(); } else { this.threads[i] = null; } } bool notFinish = true;//下载未完成 while (notFinish) {// 循环判断所有线程是否完成下载 Thread.Sleep(900); notFinish = false;//假定全部线程下载完成 for (int i = 0; i < this.threads.Length; i++) { if (this.threads[i] != null && !this.threads[i].isFinish()) {//如果发现线程未完成下载 notFinish = true;//设置标志为下载没有完成 if (this.threads[i].getDownLength() == -1) {//如果下载失败,再重新下载 this.threads[i] = new DownloadThread(this, downloadUrl, this.saveFile, this.block, this.data[i + 1], i + 1); this.threads[i].ThreadRun(); } } } if (listener != null) { listener.OnDownloadSize(this.downloadSize);//通知目前已经下载完成的数据长度 Console.WriteLine(this.downloadSize); } } } catch (Exception e) { Console.WriteLine(e.Message); throw new Exception("下载文件失败"); } return this.downloadSize; } } public interface IDownloadProgressListener { void OnDownloadSize(long size); } }