using System; using System.Collections.Generic; using System.Threading.Tasks; using System.Security.Cryptography; using System.Text; using System.IO; using UnityEngine; using UnityEngine.Networking; namespace TapTap.Common.Internal.Utils { public class ImageUtils { private readonly static string OldCacheDirName = "tap-cache"; private readonly static MD5 md5 = MD5.Create(); private readonly static Dictionary> cachedTextures = new Dictionary>(); public static async Task LoadImage(string url, int timeout = 30, bool useMemoryCache = true) { if (string.IsNullOrEmpty(url)) { TapLogger.Warn(string.Format($"ImageUtils Fetch image is null! url is null or empty!")); return null; } if (cachedTextures.TryGetValue(url, out WeakReference refTex) && refTex.TryGetTarget(out Texture tex)) { // 从内存加载 return tex; } else { try { // 从本地缓存加载 Texture cachedImage = await LoadCachedImaged(url, timeout); if (useMemoryCache) { cachedTextures[url] = new WeakReference(cachedImage); } return cachedImage; } catch (Exception e) { TapLogger.Warn(e.Message); try { // 从网络加载 Texture2D newTex = await FetchImage(url, timeout); if (useMemoryCache) { cachedTextures[url] = new WeakReference(newTex); } // 缓存到本地 _ = CacheImage(url, newTex); return newTex; } catch (Exception ex) { TapLogger.Warn(ex.Message); return null; } } } } public static async Task FetchImage(string url, int timeout = 30) { using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(url)) { request.timeout = timeout; UnityWebRequestAsyncOperation operation = request.SendWebRequest(); while (!operation.isDone) { await Task.Delay(30); } if (request.isNetworkError || request.isHttpError) { throw new Exception("Fetch image error."); } else { Texture2D texture = ((DownloadHandlerTexture)request.downloadHandler)?.texture; if (texture == null) { TapLogger.Warn($"ImageUtils Fetch image is null! url: {url}"); } return texture; } } } static async Task LoadCachedImaged(string url, int timeout = 30) { string cachedImagePath = GetCachedPath(url); if (!File.Exists(cachedImagePath)) { throw new Exception("No cached image."); } string cachedImageUrl = $"file://{cachedImagePath}"; using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(cachedImageUrl)) { request.timeout = timeout; UnityWebRequestAsyncOperation operation = request.SendWebRequest(); while (!operation.isDone) { await Task.Delay(30); } if (request.isNetworkError || request.isHttpError) { RemoveCachedImage(cachedImagePath); throw new Exception("Load cache image error."); } else { var texture = ((DownloadHandlerTexture)request.downloadHandler)?.texture; if (texture == null) { RemoveCachedImage(cachedImagePath); throw new Exception("Cached image is invalid."); } return texture; } } } static async Task CacheImage(string url, Texture2D tex) { string cacheImagePath = GetCachedPath(url); // 写入缓存 byte[] imageData = tex.EncodeToPNG(); using (FileStream fileStream = new FileStream(cacheImagePath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true)) { await fileStream.WriteAsync(imageData, 0, imageData.Length); } } static void RemoveCachedImage(string cachedImagePath) { try { File.Delete(cachedImagePath); } finally { } } static string ToHex(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.Length; i++) { sb.Append(bytes[i].ToString("x2")); } return sb.ToString(); } static string GetCachedPath(string url) { string cachedHashName = ToHex(md5.ComputeHash(Encoding.UTF8.GetBytes(url))); return Path.Combine(CacheDirPath, cachedHashName); } static string CacheDirPath { get { string newCacheDirPath = Path.Combine(Application.persistentDataPath, OldCacheDirName); if (TapCommon.Config != null && !string.IsNullOrEmpty(TapCommon.Config.ClientID) ){ newCacheDirPath = Path.Combine(Application.persistentDataPath, OldCacheDirName + "_" + TapCommon.Config.ClientID); } if(!Directory.Exists(newCacheDirPath)) { string oldPath = Path.Combine(Application.persistentDataPath, OldCacheDirName); if (Directory.Exists(oldPath)) { Directory.Move(oldPath, newCacheDirPath); } } if (!Directory.Exists(newCacheDirPath)) { Directory.CreateDirectory(newCacheDirPath); } return newCacheDirPath; } } } }