using System; using System.IO; using UnityEngine; using System.Text; using System.Collections; using UnityEngine.Networking; using System.Threading.Tasks; using System.Threading; using System.Collections.Generic; namespace XRTool.Util { /// /// 文件工具类 /// 下载,写入文件,断点续传,边下边存 /// 遍历,拷贝文件 /// 获取文件的MD5等 /// public class DataFileUtil { /// /// 预估的写入文件的速度,与真实速度无关,仅仅是模拟评估,预计写入用时 /// public const float WriteSpeed = 1024 * 1024 * 150f; ///// ///// 写入缓存的大小,如果越小,可能会引起卡顿(频繁访问数据) ///// //public const int CacheSave = 1024 * 1024 * 2; /// /// 图片格式 /// public static string[] TexturesType = new string[] { "BMP", "EXR", "GIF", "HDR", "IFF", "JPG", "PICT", "PNG", "PSD", "TGA", "TIFF" }; /// /// 视频格式 /// public static string[] MoviesType = new string[] { "MOV", "MPG", "MP4", "AVI", "ASF" }; /// /// 音频格式 /// public static string[] AudiosType = new string[] { "ACC", "AIFF", "IT", "MOD", "MPEG", "OGGVORBIS", "S3M", "AIF", "WAV", "MP3", "OGG","XM","XMA","VAG","AUDIOQUEUE" }; public static int maxTryCount = 5; public const string Extension = ".downing"; ///// ///// 断点下载,异步读写 ///// ///// ///// ///// ///// ///// ///// //public static IEnumerator DownLoadDataAsync(string url, string path, // Action completeData, DownloadHandler hander = null, Action downLoadProcess = null) //{ // yield return 0; //} /// /// 下载数据到本地,并将下载的数据返回给到上层应用 /// 此下载仅应用于可缓存的资源,图片文本等下载,大文件的下载请使用断点下载 /// /// /// /// /// /// /// public static IEnumerator DownLoadData(string url, string path, Action completeData, DownloadHandler hander = null, Action downLoadProcess = null, bool isAsync = false) { UnityWebRequest web = null; //DownloadHandlerTexture yield return RequestDownData(url, (string error, UnityWebRequest req) => { ///下载成功后开始拷贝数据到本地 if (string.IsNullOrEmpty(error)) { ///同步存储方法 if (!isAsync) { SaveDataToFile(path, req.downloadHandler.data); downLoadProcess?.Invoke(req.downloadHandler.data.Length, 1); completeData?.Invoke(path, req); return; } else { web = req; ///此时,已下载到内存中,但是仍未缓存至内存中,内存数据可用 completeData?.Invoke(null, req); } } else { ///下载出错 completeData?.Invoke(null, null); //UnityLog.LogError(error); } }, hander, (float allLeng, float process) => { ///下载的进度,1/1.2,给存储留进度条显示,约等于0.89 downLoadProcess?.Invoke(allLeng, process / 1.12f); }); ///等待下载完成,此下载是下载到内存中 if (isAsync && web != null) { //completeData?.Invoke(true, data); yield return SaveDataToFile(path, web.downloadHandler.data, (float saveProcess) => { ///更新存储文件的进度 downLoadProcess?.Invoke(web.downloadHandler.data.Length, saveProcess * 0.1f + 0.9f); }, (bool isSuccess, string filePath) => { completeData?.Invoke(filePath, web); }); } web.Dispose(); } /// /// 请求后台数据接口 /// /// public static IEnumerator RequestData(string url, Action reqComplete, bool isPost = true, Dictionary header = null, Dictionary form = null, Action complete = null) { ///表单 using (UnityWebRequest web = isPost ? UnityWebRequest.Post(url, form) : UnityWebRequest.Get(url)) { if (header != null) { foreach (var item in header) { web.SetRequestHeader(item.Key, item.Value); } } yield return web.SendWebRequest(); if (web.isNetworkError || web.isHttpError) { UnityLog.LogError("RequestData:" + url + web.error); reqComplete?.Invoke(null); complete?.Invoke(); yield break; } else { reqComplete?.Invoke(web.downloadHandler.text); complete?.Invoke(); } } } /// /// 获取资源的大小 /// /// /// /// public static IEnumerator GetDataSize(string url, Action getLen) { var headRequest = UnityWebRequest.Head(url); yield return headRequest.SendWebRequest(); getLen?.Invoke(long.Parse(headRequest.GetResponseHeader("Content-Length"))); headRequest.Dispose(); } /// /// 断点续传 /// 下载大文件请使用此函数 /// 注意,请自行检查是否已下载过此文件,此函数不再检查,直接执行下载 /// /// /// /// /// /// /// public static IEnumerator DownLoadDataAsync(string url, string path, Action completeData, DownloadHandler hander = null, Action downLoadProcess = null) { ///获取文件的大小 FileStream fs = null; long curLen = 0; string tmpPath = path + Extension; ///文件读写异常,返回错误 try { string dir = Path.GetDirectoryName(tmpPath); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } fs = new FileStream(tmpPath, FileMode.OpenOrCreate); curLen = fs.Length; UnityLog.Log("当前文件大小" + curLen, 2); } catch (Exception ex) { if (fs != null) { fs.Dispose(); } fs = null; UnityLog.LogError(tmpPath + ex.ToString()); downLoadProcess.Invoke(curLen, 0); completeData?.Invoke(null, null); yield break; } ///请求获取文件的大小 ///已下载完成,无需再次下载 long totalLength = 0; yield return GetDataSize(url, (size) => { totalLength = size; }); ///当前文件的大小和云端文件大小一致 ///理论上不会出现这个问题 if (curLen == totalLength) { fs.Dispose(); fs.Close(); downLoadProcess.Invoke(totalLength, 1); ReNameFile(tmpPath,path); completeData?.Invoke(path, null); yield break; } ///文件异常,删除文件,重新下载 ///理论上不会出现这个问题 else if (curLen > totalLength) { UnityLog.LogError("文件大小异常" + curLen + "__" + totalLength); fs.Dispose(); fs.Close(); File.Delete(tmpPath); fs = new FileStream(tmpPath, FileMode.OpenOrCreate, FileAccess.Write); curLen = fs.Length; } fs.Seek(curLen, SeekOrigin.Begin); bool isSuccess = true; yield return RequestDownData(url, (byte[] data, int len) => { if (isSuccess && data.Length > 0) { try { fs.Write(data, 0, len); fs.Flush(); curLen += len; downLoadProcess?.Invoke(curLen, curLen * 1f / totalLength); } catch (Exception ex) { UnityLog.LogError(ex.ToString()); isSuccess = false; return; } } else { ///理论上不会出现这个问题,除非出现异常情况如内存不足等 //complete?.Invoke(isSuccess, path); UnityLog.LogError("Down Error:" + url); } }, totalLength, curLen, (web) => { fs.Dispose(); fs.Close(); downLoadProcess.Invoke(totalLength, 1); ReNameFile(tmpPath,path); completeData?.Invoke(path, web); }); } /// /// Get请求下载数据,断点下载,边下载边返回数据进行存储 /// /// url /// 返回获取的数据 /// 文件的总大小 /// 已下载的数据 /// public static IEnumerator RequestDownData(string url, Action downData, long allLen, long downedLength = 0, Action complete = null) { //float downTime = Time.time; using (UnityWebRequest web = UnityWebRequest.Get(url)) { web.disposeDownloadHandlerOnDispose = true; DataDownLoadHandler ddh = new DataDownLoadHandler(); web.downloadHandler = ddh; ddh.OnReceiveData += downData; ///请求指定的长度 web.SetRequestHeader("Range", "bytes=" + downedLength + "-" + allLen); yield return web.SendWebRequest(); if (web.isNetworkError || web.isHttpError) { UnityLog.LogError("RequestDownData" + url); complete?.Invoke(null); } else { complete?.Invoke(web); } } } /// /// 从URl下载指定的资源 /// /// 资源的地址,可以是网络路径,也可以是本地文件路径 /// 下载的进度,当前已下载文件大小,以及进度 /// /// 请求的参数,如果为空代表Get请求,不为空代表Post请求 /// public static IEnumerator RequestDownData(string url, Action complete, DownloadHandler hander = null, Action updateProcess = null, WWWForm form = null) { UnityWebRequest web; Debug.Log("RequestDownData" + url); if (form == null) { web = UnityWebRequest.Get(url); } else { web = UnityWebRequest.Post(url, form); } if (hander != null) { web.downloadHandler = hander; } web.SendWebRequest(); while (!web.isDone) { updateProcess?.Invoke(web.downloadedBytes, web.downloadProgress); yield return 0; } if (web.isDone) { updateProcess?.Invoke(web.downloadedBytes, 1); } if (web.isNetworkError || web.isHttpError) { complete?.Invoke(web.error, web); Debug.LogError(web.error + url); } else { complete?.Invoke(null, web); } web.Dispose(); } /// /// 上传数据到服务端,并得到服务器返回的结果 /// 与下面的RequestDownData类似,但是更新上传进度,适用于文件上传 /// /// /// /// /// /// public static IEnumerator RequestUpLoadData(string url, WWWForm form, Action updateProcess, Action complete) { UnityWebRequest web = UnityWebRequest.Post(url, form); web.SendWebRequest(); while (!web.isDone) { updateProcess?.Invoke(web.uploadedBytes, web.uploadProgress); yield return 0; } if (web.isDone) { updateProcess?.Invoke(web.uploadedBytes, 1); } if (web.isNetworkError || web.isHttpError) { complete?.Invoke(web.error, web); } else { complete?.Invoke(null, web); } web.Dispose(); } /// /// 是否是忽略的文件类型 /// /// /// public static bool isIgnoreExtension(string ignoreExtension, string suffix) { string[] igs = ignoreExtension.Split('|', ' ', ','); for (int i = 0; i < igs.Length; i++) { if (igs[i] == suffix) { return true; } } return false; } /// /// 保存文本文件到本地 /// 同步方法 /// public static bool SaveDataToFile(string path, string txt) { try { if (!string.IsNullOrEmpty(txt)) { File.WriteAllText(path, txt); return true; } } catch (Exception ex) { UnityLog.LogError(path + ex.ToString()); } return false; } /// /// 保存二进制字节文件到本地,同步方法 /// /// /// public static bool SaveDataToFile(string path, byte[] data) { try { string dir = Path.GetDirectoryName(path); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } FileStream fs = new FileStream(path, FileMode.Create); fs.Write(data, 0, data.Length); fs.Dispose(); fs.Close(); return true; } catch (Exception ex) { UnityLog.LogError(path + ex.ToString()); } return false; } /// /// 异步保存文本文件 /// /// /// /// /// public static IEnumerator SaveDataToFile(string path, string txt, Action saveProcess, Action onComplete) { yield return SaveDataToFile(path, Encoding.Default.GetBytes(txt), saveProcess, onComplete); } /// /// 保存二进制字节文件到本地,异步方法 /// /// /// private static IEnumerator SaveDataToFile(string path, byte[] data, Action saveProcess, Action onComplete) { bool isSuccess = false; if (data != null && data.Length > 0) { Task task = null; FileStream fs = null; float allData = data.Length; try { fs = new FileStream(path, FileMode.Create); task = fs.WriteAsync(data, 0, data.Length); isSuccess = true; } catch (Exception ex) { UnityLog.LogError(path + ex.ToString()); path = ex.ToString(); isSuccess = false; } float tmp = 0; float allTime = data.Length / WriteSpeed; ///预计存储完成的时间 while (task != null && !task.IsCompleted) { if (tmp < allTime) { tmp += Time.deltaTime; } saveProcess?.Invoke(tmp / allTime); yield return 0; } saveProcess(1); fs.Dispose(); fs.Close(); } onComplete?.Invoke(isSuccess, path); } /// /// 广度优先,遍历文件 /// /// /// public static void FindFileBreadth(string path, Action callBack) { try { DirectoryInfo di = new DirectoryInfo(path); FileInfo[] fis = di.GetFiles(); for (int i = 0; i < fis.Length; i++) { callBack?.Invoke(fis[i].FullName); } DirectoryInfo[] dis = di.GetDirectories(); for (int j = 0; j < dis.Length; j++) { FindFileBreadth(dis[j].FullName, callBack); } } catch (Exception ex) { UnityLog.LogError(ex.ToString()); } } /// /// 深度优先,遍历文件 /// /// public static void FindFileByDepth(string dir, Action callBack) { try { DirectoryInfo d = new DirectoryInfo(dir); FileSystemInfo[] fsinfos = d.GetFileSystemInfos(); foreach (FileSystemInfo fsinfo in fsinfos) { if (fsinfo is DirectoryInfo) { FindFileByDepth(fsinfo.FullName, callBack); } else { callBack?.Invoke(fsinfo.FullName); } } } catch (Exception ex) { UnityLog.LogError(ex.ToString()); } } /// /// 获取文件MD5值 /// /// 文件绝对路径 /// MD5值 public static string GetMD5HashFromFile(string fileName) { try { FileStream file = new FileStream(fileName, FileMode.Open); System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); byte[] retVal = md5.ComputeHash(file); file.Close(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < retVal.Length; i++) { sb.Append(retVal[i].ToString("x2")); } return sb.ToString(); } catch (Exception ex) { throw new Exception("GetMD5HashFromFile() fail,error:" + ex.Message); } } /// /// 重命名文件 /// /// /// public static void ReNameFile(string file, string newFile) { if (File.Exists(file)) { try { string dir = Path.GetDirectoryName(newFile); if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } if (File.Exists(newFile)) { File.Delete(newFile); } FileInfo info = new FileInfo(file); info.MoveTo(newFile); } catch (Exception ex) { UnityLog.LogError(file + " is error" + ex.ToString()); } } } /// /// 删除文件 /// /// /// public static void DelFile(string file) { UnityLog.Log(file + " is Del", 3); if (File.Exists(file)) { try { File.Delete(file); } catch (Exception ex) { UnityLog.LogError(file + " is error" + ex.ToString()); } } } /// /// 拷贝文件 /// /// /// /// 是否覆盖 /// public static bool CopyDirectory(string SourcePath, string DestinationPath, bool overwriteexisting) { bool ret = false; try { SourcePath = SourcePath.EndsWith(@"\") ? SourcePath : SourcePath + @"\"; DestinationPath = DestinationPath.EndsWith(@"\") ? DestinationPath : DestinationPath + @"\"; if (Directory.Exists(SourcePath)) { if (Directory.Exists(DestinationPath) == false) Directory.CreateDirectory(DestinationPath); foreach (string fls in Directory.GetFiles(SourcePath)) { FileInfo flinfo = new FileInfo(fls); flinfo.CopyTo(DestinationPath + flinfo.Name, overwriteexisting); } foreach (string drs in Directory.GetDirectories(SourcePath)) { DirectoryInfo drinfo = new DirectoryInfo(drs); if (CopyDirectory(drs, DestinationPath + drinfo.Name, overwriteexisting) == false) ret = false; } } ret = true; } catch (Exception ex) { ret = false; UnityLog.LogError(ex.ToString()); } return ret; } /// /// 文件是否是图片 /// /// /// public static bool IsMoviesFile(string fileName) { if (!string.IsNullOrEmpty(fileName)) { string exten = Path.GetExtension(fileName); return IsMediaType(exten.ToUpper(), MoviesType); } return false; } public static bool IsTextureFile(string fileName) { if (!string.IsNullOrEmpty(fileName)) { string exten = Path.GetExtension(fileName); return IsMediaType(exten.ToUpper(), TexturesType); } return false; } /// /// 判断是否是音频文件 /// MP3,WAV等 /// /// /// public static bool IsAudioFile(string fileName) { if (!string.IsNullOrEmpty(fileName)) { string exten = Path.GetExtension(fileName); return IsMediaType(exten.ToUpper(), AudiosType); } return false; } /// /// 是否是多媒体文件:图片,视频,音频 /// /// /// /// public static bool IsMediaType(string fileName, string[] types) { if (!string.IsNullOrEmpty(fileName) && types != null) { for (int i = 0; i < types.Length; i++) { if (TexturesType[i] == fileName) { return true; } } } return false; } /// /// 是否多媒体文件 /// 音频,视频,图片 /// /// /// public static bool IsMediaType(string fileName) { if (!string.IsNullOrEmpty(fileName)) { string exten = Path.GetExtension(fileName); exten = exten.ToUpper(); return IsMediaType(exten, TexturesType) || IsMediaType(exten, MoviesType) || IsMediaType(exten, AudiosType); } return false; } /// /// 是否存在某文件 /// /// /// public static bool Exists(string file) { return File.Exists(file); } } }