ImageUtils.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Threading.Tasks;
  4. using System.Security.Cryptography;
  5. using System.Text;
  6. using System.IO;
  7. using UnityEngine;
  8. using UnityEngine.Networking;
  9. using TapSDK.Core.Internal.Log;
  10. namespace TapSDK.Core.Internal.Utils {
  11. public class ImageUtils {
  12. private readonly static string OldCacheDirName = "tap-cache";
  13. private readonly static MD5 md5 = MD5.Create();
  14. private readonly static Dictionary<string, WeakReference<Texture>> cachedTextures = new Dictionary<string, WeakReference<Texture>>();
  15. public static async Task<Texture> LoadImage(string url, int timeout = 30, bool useMemoryCache = true) {
  16. if (string.IsNullOrEmpty(url)) {
  17. TapLog.Warning(string.Format($"ImageUtils Fetch image is null! url is null or empty!"));
  18. return null;
  19. }
  20. if (cachedTextures.TryGetValue(url, out WeakReference<Texture> refTex) &&
  21. refTex.TryGetTarget(out Texture tex)) {
  22. // 从内存加载
  23. return tex;
  24. } else {
  25. try {
  26. // 从本地缓存加载
  27. Texture cachedImage = await LoadCachedImaged(url, timeout);
  28. if (useMemoryCache) {
  29. cachedTextures[url] = new WeakReference<Texture>(cachedImage);
  30. }
  31. return cachedImage;
  32. } catch (Exception e) {
  33. TapLog.Warning(e.Message);
  34. try {
  35. // 从网络加载
  36. Texture2D newTex = await FetchImage(url, timeout);
  37. if (useMemoryCache) {
  38. cachedTextures[url] = new WeakReference<Texture>(newTex);
  39. }
  40. // 缓存到本地
  41. _ = CacheImage(url, newTex);
  42. return newTex;
  43. } catch (Exception ex) {
  44. TapLog.Warning(ex.Message);
  45. return null;
  46. }
  47. }
  48. }
  49. }
  50. public static async Task<Texture2D> FetchImage(string url, int timeout = 30) {
  51. using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(url)) {
  52. request.timeout = timeout;
  53. UnityWebRequestAsyncOperation operation = request.SendWebRequest();
  54. while (!operation.isDone) {
  55. await Task.Delay(30);
  56. }
  57. if (request.isNetworkError || request.isHttpError) {
  58. throw new Exception("Fetch image error.");
  59. } else {
  60. Texture2D texture = ((DownloadHandlerTexture)request.downloadHandler)?.texture;
  61. if (texture == null) {
  62. TapLog.Warning($"ImageUtils Fetch image is null! url: {url}");
  63. }
  64. return texture;
  65. }
  66. }
  67. }
  68. static async Task<Texture> LoadCachedImaged(string url, int timeout = 30) {
  69. string cachedImagePath = GetCachedPath(url);
  70. if (!File.Exists(cachedImagePath)) {
  71. throw new Exception("No cached image.");
  72. }
  73. string cachedImageUrl = $"file://{cachedImagePath}";
  74. using (UnityWebRequest request = UnityWebRequestTexture.GetTexture(cachedImageUrl)) {
  75. request.timeout = timeout;
  76. UnityWebRequestAsyncOperation operation = request.SendWebRequest();
  77. while (!operation.isDone) {
  78. await Task.Delay(30);
  79. }
  80. if (request.isNetworkError || request.isHttpError) {
  81. RemoveCachedImage(cachedImagePath);
  82. throw new Exception("Load cache image error.");
  83. } else {
  84. var texture = ((DownloadHandlerTexture)request.downloadHandler)?.texture;
  85. if (texture == null) {
  86. RemoveCachedImage(cachedImagePath);
  87. throw new Exception("Cached image is invalid.");
  88. }
  89. return texture;
  90. }
  91. }
  92. }
  93. static async Task CacheImage(string url, Texture2D tex) {
  94. string cacheImagePath = GetCachedPath(url);
  95. // 写入缓存
  96. byte[] imageData = tex.EncodeToPNG();
  97. using (FileStream fileStream = new FileStream(cacheImagePath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 4096, useAsync: true)) {
  98. await fileStream.WriteAsync(imageData, 0, imageData.Length);
  99. }
  100. }
  101. static void RemoveCachedImage(string cachedImagePath) {
  102. try {
  103. File.Delete(cachedImagePath);
  104. } finally {
  105. }
  106. }
  107. static string ToHex(byte[] bytes) {
  108. StringBuilder sb = new StringBuilder();
  109. for (int i = 0; i < bytes.Length; i++) {
  110. sb.Append(bytes[i].ToString("x2"));
  111. }
  112. return sb.ToString();
  113. }
  114. static string GetCachedPath(string url) {
  115. string cachedHashName = ToHex(md5.ComputeHash(Encoding.UTF8.GetBytes(url)));
  116. return Path.Combine(CacheDirPath, cachedHashName);
  117. }
  118. static string CacheDirPath {
  119. get {
  120. string newCacheDirPath = Path.Combine(Application.persistentDataPath, OldCacheDirName);
  121. if (TapTapSDK.taptapSdkOptions != null && !string.IsNullOrEmpty(TapTapSDK.taptapSdkOptions.clientId) ){
  122. newCacheDirPath = Path.Combine(Application.persistentDataPath, OldCacheDirName + "_" + TapTapSDK.taptapSdkOptions.clientId);
  123. }
  124. if(!Directory.Exists(newCacheDirPath)) {
  125. string oldPath = Path.Combine(Application.persistentDataPath, OldCacheDirName);
  126. if (Directory.Exists(oldPath)) {
  127. Directory.Move(oldPath, newCacheDirPath);
  128. }
  129. }
  130. if (!Directory.Exists(newCacheDirPath)) {
  131. Directory.CreateDirectory(newCacheDirPath);
  132. }
  133. return newCacheDirPath;
  134. }
  135. }
  136. }
  137. }