using UnityEngine; using UnityEngine.UI; using System.Collections; using System.IO; using System.Collections.Generic; using System.Security.Policy; using System; using UnityEngine.Networking; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; using TEngine; using WebP; public class AsyncImageDownload : MonoBehaviour { private Dictionary sprites = new Dictionary(); public Dictionary textures = new Dictionary(); private Sprite myWXPic; private static AsyncImageDownload _instance = null; public static AsyncImageDownload GetInstance() { return Instance; } public static AsyncImageDownload Instance { get { if (_instance == null) { GameObject obj = new GameObject("AsyncImageDownload"); _instance = obj.AddComponent(); DontDestroyOnLoad(obj); _instance.Init(); } return _instance; } } public void CleanMemory() { this.sprites.Clear(); this.textures.Clear(); } public bool Init() { if (!Directory.Exists(Application.persistentDataPath + "/ImageCache/")) { Directory.CreateDirectory(Application.persistentDataPath + "/ImageCache/"); } myWXPic = null; return true; } public static string GetBigMd5(string str) { string cl = str; string pwd = ""; var md5 = MD5.Create(); byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(cl)); for (int i = 0; i < s.Length; i++) { pwd = pwd + s[i].ToString("X2"); } return pwd; } public async Task SetAsyncImage(string url, RawImage image) { // 神秘人头像不缓存 if (url == "smr") { GameModule.Resource.LoadAsset("smr", (tex) => { if (image != null) { image.texture = tex; } }); return; } var id = GetBigMd5(url); if (textures.ContainsKey(id)) { if (image != null) { image.texture = textures[id]; } return; } if (!File.Exists(path + id)) { await DownloadImageRaw(id, url, image); } else { await LoadLocalImageRaw(id, url, image); } } public async Task SetAsyncImage(string url, SpriteRenderer spriteRenderer) { // Check if the URL is for a special case if (url == "smr") { GameModule.Resource.LoadAsset("smr", (tex) => { if (spriteRenderer != null) { spriteRenderer.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f)); } }); return; } var id = GetBigMd5(url); if (textures.ContainsKey(id)) { if (spriteRenderer != null) { var tex = textures[id]; var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f)); sprite.name = id; spriteRenderer.sprite = sprite; } return; } if (!File.Exists(path + id)) { await DownloadImageSprite(id, url, spriteRenderer); } else { await LoadLocalImageSprite(id, url, spriteRenderer); } } public async Task SetAsyncImage(string url, Image image) { // Check if the URL is for a special case if (url == "smr") { GameModule.Resource.LoadAsset("smr", (tex) => { if (image != null) { var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f)); image.sprite = sprite; } }); return; } var id = GetBigMd5(url); if (textures.ContainsKey(id)) { if (image != null) { var tex = textures[id]; var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f)); sprite.name = id; // 设置Sprite的名称 image.sprite = sprite; } return; } if (!File.Exists(GetFilePath(id))) { await DownloadImageForImage(id, url, image); } else { await LoadLocalImageForImage(id, url, image); } } //int index,Action act IEnumerator DownloadImageRaw2(string id, string url, RawImage image, int index, Action act) { using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url)) { yield return www.SendWebRequest(); if (www.result != UnityWebRequest.Result.Success) { //Debug.LogError(url+" "+www.result); act?.Invoke(index, url, null); } else { Texture2D tex2d = DownloadHandlerTexture.GetContent(www); //www.texture; if (tex2d != null) { if (image != null) { image.texture = tex2d; } textures[id] = tex2d; //将图片保存至缓存路径 byte[] pngData = tex2d.EncodeToPNG();//将材质压缩成byte流 File.WriteAllBytes(path + id, pngData); } act?.Invoke(index, url, tex2d); } } } private async Task DownloadImageRaw(string id, string url, RawImage image) { using (UnityWebRequest www = UnityWebRequest.Get(url)) { var asyncOp = www.SendWebRequest(); while (!asyncOp.isDone) { await Task.Yield(); } if (www.result != UnityWebRequest.Result.Success) { if (image != null) { image.texture = null; } } else { byte[] data = www.downloadHandler.data; // 在解码前获取图片尺寸 var (originalWidth, originalHeight) = GetImageSize(data); // 计算限制大小后的尺寸 var (targetWidth, targetHeight) = CalculateTargetSize(originalWidth, originalHeight, 100, 100); // Debug.Log($"Image size before decode: {originalWidth}x{originalHeight}, target size: {targetWidth}x{targetHeight} from URL: {url}"); Texture2D tex2d = null; if (IsWebP(data)) { tex2d = DecodeWebP(data, targetWidth, targetHeight); } else { tex2d = new Texture2D(targetWidth, targetHeight); tex2d.LoadImage(data); } if (tex2d != null) { if (image != null) { image.texture = tex2d; } if (!textures.ContainsKey(id)) { textures[id] = tex2d; } byte[] pngData = tex2d.EncodeToPNG(); File.WriteAllBytes(path + id, pngData); } else { if (image != null) { image.texture = null; } } } } } // 计算目标尺寸,保持宽高比并限制最大尺寸 private (int width, int height) CalculateTargetSize(int originalWidth, int originalHeight, int maxWidth, int maxHeight) { if (originalWidth <= 0 || originalHeight <= 0) return (2, 2); // 默认最小尺寸 if (originalWidth <= maxWidth && originalHeight <= maxHeight) return (originalWidth, originalHeight); // 如果原始尺寸已经在限制范围内,直接使用 float ratio = Mathf.Min( maxWidth / (float)originalWidth, maxHeight / (float)originalHeight ); int targetWidth = Mathf.Max(2, Mathf.RoundToInt(originalWidth * ratio)); int targetHeight = Mathf.Max(2, Mathf.RoundToInt(originalHeight * ratio)); return (targetWidth, targetHeight); } // 检查是否是WebP格式 private bool IsWebP(byte[] data) { if (data.Length < 12) return false; // WebP文件头标识:"RIFF" + 4字节 + "WEBP" return data[0] == 'R' && data[1] == 'I' && data[2] == 'F' && data[3] == 'F' && data[8] == 'W' && data[9] == 'E' && data[10] == 'B' && data[11] == 'P'; } // 使用SimpleWebP解码WebP图片 private Texture2D DecodeWebP(byte[] data, int width, int height) { try { // 使用SimpleWebP插件解码 // Texture2D texture = SimpleWebP.Load(data); // var texutre = Texture2DExt.CreateTexture2DFromWebP(data,false,false,(e)=>{}); Texture2D texture = new Texture2D(width, height); texture.LoadWebP(data, out Error error); return texture; } catch (Exception e) { Debug.LogError($"WebP decode error: {e.Message}"); return null; } } // 这里有很大的GC隐患,概率卡顿的问题在这 IEnumerator LoadLocalImageRaw2(string id, string url, RawImage image, int index, Action act) { string filePath = "file:///" + path + id; //Debug.LogError(filePath); //WWW www = new WWW(filePath); //yield return www; //Texture2D texture = www.texture; //textures[id] = texture; ////image.texture = texture; //act?.Invoke(index, url, texture); //www.Dispose(); //string filePath = path + url.GetHashCode(); using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(filePath)) { yield return www.SendWebRequest(); Texture2D texture = DownloadHandlerTexture.GetContent(www); textures[id] = texture; //image.texture = texture; act?.Invoke(index, url, texture); } } // 这里有很大的GC隐患,概率卡顿的问题在这 private async Task LoadLocalImageRaw(string id, string url, RawImage image) { string filePath = "file:///" + path + id; using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(filePath)) { var asyncOp = www.SendWebRequest(); while (!asyncOp.isDone) { await Task.Yield(); } Texture2D texture = DownloadHandlerTexture.GetContent(www); if (!textures.ContainsKey(id)) { textures[id] = texture; } if (image != null) { image.texture = texture; } } } public string path { get { //pc,ios //android :jar:file// return Application.persistentDataPath + "/ImageCache/"; } } public async Task PreCacheImage(string url) { // 神秘人头像不缓存 if (url == "smr") { return; } var id = GetBigMd5(url); if (textures.ContainsKey(id)) { // 图像已经缓存,无需再次下载 return; } if (!File.Exists(path + id)) { await DownloadImageRaw(id, url, null); } else { await LoadLocalImageRaw(id, url, null); } } private (int width, int height) GetImageSize(byte[] data) { if (IsWebP(data)) { return GetWebPSize(data); } else if (IsPNG(data)) { return GetPNGSize(data); } else if (IsJPG(data)) { return GetJPGSize(data); } return (0, 0); } private bool IsPNG(byte[] data) { if (data.Length < 8) return false; return data[0] == 0x89 && data[1] == 0x50 && data[2] == 0x4E && data[3] == 0x47; } private (int width, int height) GetPNGSize(byte[] data) { if (data.Length < 24) return (0, 0); int width = (data[16] << 24) | (data[17] << 16) | (data[18] << 8) | data[19]; int height = (data[20] << 24) | (data[21] << 16) | (data[22] << 8) | data[23]; return (width, height); } private bool IsJPG(byte[] data) { if (data.Length < 2) return false; return data[0] == 0xFF && data[1] == 0xD8; } private (int width, int height) GetJPGSize(byte[] data) { int pos = 2; while (pos < data.Length - 2) { if (data[pos] != 0xFF) return (0, 0); if (data[pos + 1] == 0xC0 || data[pos + 1] == 0xC2) { int height = (data[pos + 5] << 8) | data[pos + 6]; int width = (data[pos + 7] << 8) | data[pos + 8]; return (width, height); } pos += 2 + ((data[pos + 2] << 8) | data[pos + 3]); } return (0, 0); } private (int width, int height) GetWebPSize(byte[] data) { if (data.Length < 30) return (0, 0); // WebP VP8 if (data[12] == 'V' && data[13] == 'P' && data[14] == '8') { int width = (data[26] | (data[27] << 8)) & 0x3FFF; int height = (data[28] | (data[29] << 8)) & 0x3FFF; return (width, height); } return (0, 0); } private async Task DownloadImageSprite(string id, string url, SpriteRenderer spriteRenderer) { using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url)) { var asyncOp = www.SendWebRequest(); while (!asyncOp.isDone) { await Task.Yield(); } if (www.result != UnityWebRequest.Result.Success) { if (spriteRenderer != null) { spriteRenderer.sprite = null; } } else { byte[] data = www.downloadHandler.data; Texture2D tex2d = new Texture2D(2, 2); tex2d.LoadImage(data); if (tex2d != null) { if (spriteRenderer != null) { var sprite = Sprite.Create(tex2d, new Rect(0, 0, tex2d.width, tex2d.height), new Vector2(0.5f, 0.5f)); sprite.name = id; spriteRenderer.sprite = sprite; } if (!textures.ContainsKey(id)) { textures[id] = tex2d; } byte[] pngData = tex2d.EncodeToPNG(); File.WriteAllBytes(path + id, pngData); } else { if (spriteRenderer != null) { spriteRenderer.sprite = null; } } } } } private async Task LoadLocalImageSprite(string id, string url, SpriteRenderer spriteRenderer) { string filePath = "file:///" + path + id; using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(filePath)) { var asyncOp = www.SendWebRequest(); while (!asyncOp.isDone) { await Task.Yield(); } Texture2D texture = DownloadHandlerTexture.GetContent(www); if (!textures.ContainsKey(id)) { textures[id] = texture; } if (spriteRenderer != null) { var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); sprite.name = id; spriteRenderer.sprite = sprite; } } } public void SetSyncImage(string url, RawImage image) { if (url == "smr") { GameModule.Resource.LoadAsset("smr", (tex) => { if (image != null) { image.texture = tex; } }); return; } var id = GetBigMd5(url); if (textures.ContainsKey(id)) { if (image != null) { image.texture = textures[id]; } return; } if (!File.Exists(path + id)) { DownloadImageRawSync(id, url, image); } else { LoadLocalImageRawSync(id, url, image); } } private void DownloadImageRawSync(string id, string url, RawImage image) { using (UnityWebRequest www = UnityWebRequest.Get(url)) { www.SendWebRequest(); while (!www.isDone) { } if (www.result != UnityWebRequest.Result.Success) { if (image != null) { image.texture = null; } } else { byte[] data = www.downloadHandler.data; Texture2D tex2d = new Texture2D(2, 2); tex2d.LoadImage(data); if (tex2d != null) { if (image != null) { image.texture = tex2d; } if (!textures.ContainsKey(id)) { textures[id] = tex2d; } byte[] pngData = tex2d.EncodeToPNG(); File.WriteAllBytes(path + id, pngData); } else { if (image != null) { image.texture = null; } } } } } private void LoadLocalImageRawSync(string id, string url, RawImage image) { string filePath = "file:///" + path + id; using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(filePath)) { www.SendWebRequest(); while (!www.isDone) { } Texture2D texture = DownloadHandlerTexture.GetContent(www); if (!textures.ContainsKey(id)) { textures[id] = texture; } if (image != null) { image.texture = texture; } } } public void SetSyncImage(string url, SpriteRenderer spriteRenderer) { if (url == "smr") { GameModule.Resource.LoadAsset("smr", (tex) => { if (spriteRenderer != null) { spriteRenderer.sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f)); } }); return; } var id = GetBigMd5(url); if (textures.ContainsKey(id)) { if (spriteRenderer != null) { var tex = textures[id]; var sprite = Sprite.Create(tex, new Rect(0, 0, tex.width, tex.height), new Vector2(0.5f, 0.5f)); sprite.name = id; spriteRenderer.sprite = sprite; } return; } if (!File.Exists(path + id)) { DownloadImageSpriteSync(id, url, spriteRenderer); } else { LoadLocalImageSpriteSync(id, url, spriteRenderer); } } private void DownloadImageSpriteSync(string id, string url, SpriteRenderer spriteRenderer) { using (UnityWebRequest www = UnityWebRequest.Get(url)) { www.SendWebRequest(); while (!www.isDone) { } if (www.result != UnityWebRequest.Result.Success) { if (spriteRenderer != null) { spriteRenderer.sprite = null; } } else { byte[] data = www.downloadHandler.data; Texture2D tex2d = new Texture2D(2, 2); tex2d.LoadImage(data); if (tex2d != null) { if (spriteRenderer != null) { var sprite = Sprite.Create(tex2d, new Rect(0, 0, tex2d.width, tex2d.height), new Vector2(0.5f, 0.5f)); sprite.name = id; spriteRenderer.sprite = sprite; } if (!textures.ContainsKey(id)) { textures[id] = tex2d; } byte[] pngData = tex2d.EncodeToPNG(); File.WriteAllBytes(path + id, pngData); } else { if (spriteRenderer != null) { spriteRenderer.sprite = null; } } } } } private void LoadLocalImageSpriteSync(string id, string url, SpriteRenderer spriteRenderer) { string filePath = "file:///" + path + id; using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(filePath)) { www.SendWebRequest(); while (!www.isDone) { } Texture2D texture = DownloadHandlerTexture.GetContent(www); if (!textures.ContainsKey(id)) { textures[id] = texture; } if (spriteRenderer != null) { var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); sprite.name = id; spriteRenderer.sprite = sprite; } } } private async Task DownloadImageForImage(string id, string url, Image image) { using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(url)) { var asyncOp = www.SendWebRequest(); while (!asyncOp.isDone) { await Task.Yield(); } if (www.result != UnityWebRequest.Result.Success) { if (image != null) { image.sprite = null; } } else { byte[] data = www.downloadHandler.data; Texture2D tex2d = new Texture2D(2, 2); tex2d.LoadImage(data); if (tex2d != null) { if (image != null) { var sprite = Sprite.Create(tex2d, new Rect(0, 0, tex2d.width, tex2d.height), new Vector2(0.5f, 0.5f)); sprite.name = id; // 设置Sprite的名称 image.sprite = sprite; } if (!textures.ContainsKey(id)) { textures[id] = tex2d; } byte[] pngData = tex2d.EncodeToPNG(); File.WriteAllBytes(GetFilePath(id), pngData); } else { if (image != null) { image.sprite = null; } } } } } private async Task LoadLocalImageForImage(string id, string url, Image image) { string filePath = "file:///" + GetFilePath(id); using (UnityWebRequest www = UnityWebRequestTexture.GetTexture(filePath)) { var asyncOp = www.SendWebRequest(); while (!asyncOp.isDone) { await Task.Yield(); } Texture2D texture = DownloadHandlerTexture.GetContent(www); if (!textures.ContainsKey(id)) { textures[id] = texture; } if (image != null) { var sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height), new Vector2(0.5f, 0.5f)); sprite.name = id; // 设置Sprite的名称 image.sprite = sprite; } } } private string GetFilePath(string id) { return path + id; } }