using System; using System.Net; using System.IO; using System.Text; using System.Threading; using System.Collections; using System.Net.Security; using System.Collections.Generic; using UnityEngine; using GameCore; using System.Text.RegularExpressions; namespace ResUpdate { public enum DownLoadType { NewFile, CacheResumeFile, } public enum DownloadState { None, Init, Loading, Finished } /// /// 线程下载器 /// public class ThreadDownloader { /// /// 正则表达式 /// public static Regex httpsReg = new Regex("^https.*"); /// /// 默认的缓冲区大小 /// const int DEFAULT_BUFFER_SIZE = 1024 * 64; // 64k /// /// 超时时间 /// const uint MAX_WAIT_TIME = 30 * 1000; /// /// 下载进度 /// public DownLoadProgress progress { get; private set; } public float speed { get; private set; } /// /// 下载的URL /// public string url { get; private set; } /// /// 文件保存路径 /// public string savePath { get; private set; } /// /// 本地缓存文件 /// public string fileLocalCache { get; private set; } /// /// 下载文件的大小 /// public long fileSize { get; private set; } /// /// 下载文件的CRC /// public string fileCRC { get; private set; } /// /// 文件名 /// public string fileName { get; private set; } /// /// 是否成功 /// public bool isSuccess { get; private set; } /// /// 本地已经下载的大小 /// public long localByteSize { get { return bytesSize + localBytes; } } /// /// 当前下载的大小 /// public long bytesSize { get; private set; } /// /// 下载状态 /// public DownloadState downLoadState { get; private set; } /// /// 下载类型 /// public DownLoadType downLoadType { get; private set; } /// /// 错误信息 /// public string errorMsg { get; private set; } /// /// readBuffer /// byte[] readBuffer; /// /// 总共需要下载的大小 /// long totalBytes; /// /// 本地已经下载的大小 /// long localBytes ; /// /// 上下文 /// byte[] content; /// /// 请求参数 /// string postContent; /// /// http请求 /// HttpWebRequest webRequest; /// /// http响应 /// HttpWebResponse webResponse; /// /// /// Stream responseStream; /// /// /// Stream destStream; /// /// 完成回调 /// Action finishAction; /// /// 进度回调 /// Action progressAction; /// /// 下载线程 /// Thread thread = null; DateTime startTime ; /// /// 初始化 /// public void Init() { if (progress == null) progress = new DownLoadProgress(); if (readBuffer == null) readBuffer = new byte[DEFAULT_BUFFER_SIZE]; } /// /// 初始化参数 /// /// 文件名 /// 下载链接 /// 文件保存路径 /// 进度回调 /// 下载完成回调 /// http参数 public void Init(string fileName, string url, string savePath, Action progressAction = null, Action finishAction = null, string posContent = "") { this.downLoadState = DownloadState.Init; this.downLoadType = DownLoadType.NewFile; this.fileName = fileName; this.url = url; this.savePath = savePath; this.progressAction = progressAction; this.finishAction = finishAction; this.postContent = posContent; } /// /// 初始化参数 /// /// 文件名 /// 下载链接 /// 文件保存路径 /// /// /// /// /// public void Init(string fileName, string url, string savePath, long size, string crc, Action progressAction = null, Action finishAction = null, string posContent = "") { this.downLoadState = DownloadState.Init; this.downLoadType = DownLoadType.CacheResumeFile; this.url = url; this.fileName = fileName; this.savePath = savePath; this.fileSize = size; this.fileCRC = crc; this.progressAction = progressAction; this.finishAction = finishAction; this.postContent = posContent; } /// /// 开始多线程下载 /// public void Start() { thread = new Thread(Download); thread.IsBackground = true; thread.Start(); } /// /// 开始下载 /// void Download() { isSuccess = false; downLoadState = DownloadState.Loading; startTime = DateTime.Now; try { if (!string.IsNullOrEmpty(httpsReg.Match(url).ToString())) { ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ResourceDownloadManager.CheckValidationResult); } var stringBuilder = new StringBuilder(); var fullUrl = stringBuilder.Append(url).Append(fileName).ToString(); webRequest = (HttpWebRequest)WebRequest.Create(fullUrl); Debug.LogError("url :" + fullUrl); if (downLoadType == DownLoadType.CacheResumeFile) { stringBuilder.Length = 0; fileLocalCache = stringBuilder.Append(savePath).Append(UpdateConfigs.TMP_SUFFIX).ToString(); localBytes = FileUtil.GetFileBytesSize(fileLocalCache); webRequest.AddRange((int)localBytes); } else { FileUtil.DeleteFile(savePath); } webRequest.ProtocolVersion = HttpVersion.Version11; if (!string.IsNullOrEmpty(postContent)) { webRequest.Method = "POST"; webRequest.ContentType = "application/x-www-form-urlencoded;charset=UTF-8"; content = Encoding.UTF8.GetBytes(postContent); webRequest.ContentLength = content.Length; } else { var ar = webRequest.BeginGetResponse(new AsyncCallback(BeginGetResponseCallback), null); ThreadPool.RegisterWaitForSingleObject(ar.AsyncWaitHandle, new WaitOrTimerCallback(BeginGetResponseTimeout), webRequest, MAX_WAIT_TIME, true); } } catch (WebException e) { if (webRequest != null) webRequest.Abort(); errorMsg = "web exception"; FinishDownload(false); if (BaseLogger.isDebug) Debug.LogWarning(string.Format("BeginGetResponse web exception. message: {0}, status: {1}.", e.Message, e.Status)); } catch (Exception e) { if (webRequest != null) webRequest.Abort(); errorMsg = "download exception"; FinishDownload(false); if (BaseLogger.isDebug) Debug.LogWarning(string.Format("BeginGetResponse exception. source: {0}, message: {1}.", e.Source, e.Message)); } } /// /// 获取响应超时 /// /// /// void BeginGetResponseTimeout(object state, bool isTimedOut) { if (!isTimedOut) { return; } errorMsg = "response timeout"; FinishDownload(false); } /// /// 获取响应异步回调 /// /// void BeginGetResponseCallback(IAsyncResult asynchronousResult) { try { speed = 0; webResponse = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult); totalBytes = webResponse.ContentLength; if (fileSize != 0 && fileSize != totalBytes + localBytes) // TODO: 校验文件大小! { if (BaseLogger.isDebug) Debug.LogWarning(string.Format("Http file invalid File {0} size {1}, expected {2}!", fileName, totalBytes, fileSize)); errorMsg = "size verify failed"; FinishDownload(false); return; } destStream = FileUtil.ForceOpenFileStream(downLoadType == DownLoadType.CacheResumeFile ? fileLocalCache : savePath); if (destStream == null) { errorMsg = "open file error"; FinishDownload(false); return; } if (downLoadType == DownLoadType.CacheResumeFile) destStream.Seek(destStream.Length, SeekOrigin.Current); responseStream = webResponse.GetResponseStream(); if (responseStream != null) { var ar = responseStream.BeginRead(readBuffer, 0, readBuffer.Length, ReadCallBack, null); var signalled = ar.AsyncWaitHandle.WaitOne((int) MAX_WAIT_TIME); BeginReadTimeOut(null, !signalled); } } catch (Exception e) { errorMsg = "response callback exception"; FinishDownload(false); if (BaseLogger.isDebug) Debug.LogError(string.Format("ResCallback exception. message: {0}.", e.Message)); } } /// /// 读取数据回调 /// /// void ReadCallBack(IAsyncResult asyncResult) { try { var bytesRead = responseStream.EndRead(asyncResult); destStream.Write(readBuffer, 0, bytesRead); if (bytesRead > 0) { bytesSize += bytesRead; TimeSpan span = DateTime.Now - startTime; float second = (float)span.TotalSeconds; if (second > 0.0001) { speed = bytesSize / 1024 / second; } var ar = responseStream.BeginRead(readBuffer, 0, readBuffer.Length, ReadCallBack, null); var signalled = ar.AsyncWaitHandle.WaitOne((int)MAX_WAIT_TIME); BeginReadTimeOut(null, !signalled); return; } else { destStream.Flush(); FinishDownload(bytesSize == totalBytes); } } catch (Exception ex) { if (BaseLogger.isDebug) Debug.LogWarning(string.Format("ReadCallBack EndRead exception. message: {0}.", ex.Message)); errorMsg = "read bytes exception"; FinishDownload(false); } } void BeginReadTimeOut(object state, bool isTimedOut) { if (!isTimedOut) { return; } errorMsg = "read timeout"; if (BaseLogger.isDebug) Debug.LogWarning(string.Format("Failed to download file {0}, BeginRead timeout!", fileName)); FinishDownload(false); } /// /// 下载结束 /// /// 是否下载成功 void FinishDownload(bool tmpSuccess) { lock (this) { if (downLoadState != DownloadState.Loading) return; if (webResponse != null) webResponse.Close(); if (responseStream != null) responseStream.Close(); if (destStream != null) destStream.Close(); var isVerifySuccess = true; if (tmpSuccess) { if (downLoadType == DownLoadType.CacheResumeFile) { if (string.Empty != fileCRC) { var localCrc = FileToCRC32.GetFileCRC32(fileLocalCache); if (localCrc == fileCRC) { FileUtil.MoveFile(fileLocalCache, savePath); if (BaseLogger.isDebug) Debug.Log(string.Format("Finish download success file {0}", fileName)); } else { isVerifySuccess = false; FileUtil.DeleteFile(fileLocalCache); errorMsg = "crc verify failed"; if (BaseLogger.isDebug) Debug.LogWarning(string.Format("Finish download crc check failed {0}", fileName)); } } else { FileUtil.MoveFile(fileLocalCache, savePath); if (BaseLogger.isDebug) Debug.Log(string.Format("Finish download success file {0}", fileName)); } } } else { //if (GameUpdateConfig.DEBUG) Debug.LogWarning(string.Format("Finish download failed to download file {0}", _fileName)); } this.isSuccess = tmpSuccess && isVerifySuccess; downLoadState = DownloadState.Finished; } } /// /// 进度更新回调 /// public void UpdateCallBack() { progress.UpdateProgress(localByteSize, totalBytes,speed); if (progressAction != null) { progressAction(fileName,progress); } } /// /// 下载完成回调 /// public void FinishCallback() { if (finishAction != null) finishAction(fileName,isSuccess); } /// /// 清理数据 /// public void Reset() { url = string.Empty; fileName = string.Empty; savePath = string.Empty; fileLocalCache = string.Empty; fileSize = 0; fileCRC = string.Empty; isSuccess = false; readBuffer = null; totalBytes = 0; bytesSize = 0; localBytes = 0; content = null; postContent = string.Empty; webRequest = null; webResponse = null; responseStream = null; destStream = null; finishAction = null; progressAction = null; downLoadState = DownloadState.None; errorMsg = string.Empty; thread = null; } } }