CacheableDownloadHandler.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. using UnityEngine.Networking;
  2. using System.IO;
  3. using System.Security.Cryptography;
  4. using System.Text;
  5. using System;
  6. using UnityEngine;
  7. using Logger = SongBrowser.Logging.Logger;
  8. // Modified Version of:
  9. // https://github.com/mob-sakai/AssetSystem/blob/master/Assets/Mobcast/Coffee/AssetSystem/CacheableDownloadHandler.cs
  10. // MIT-LICENSE - https://github.com/mob-sakai/AssetSystem/blob/master/LICENSE
  11. // Modified to use custom logging only.
  12. namespace Mobcast.Coffee.AssetSystem
  13. {
  14. public static class UnityWebRequestCachingExtensions
  15. {
  16. /// <summary>
  17. /// Set UnityWebRequest to be cacheable(Etag).
  18. /// </summary>
  19. public static void SetCacheable(this UnityWebRequest www, CacheableDownloadHandler handler)
  20. {
  21. var etag = CacheableDownloadHandler.GetCacheEtag(www.url);
  22. if (etag != null)
  23. www.SetRequestHeader("If-None-Match", etag);
  24. www.downloadHandler = handler;
  25. }
  26. }
  27. /// <summary>
  28. /// Cacheable download handler texture.
  29. /// </summary>
  30. public class CacheableDownloadHandlerTexture : CacheableDownloadHandler
  31. {
  32. Texture2D m_Texture;
  33. public CacheableDownloadHandlerTexture(UnityWebRequest www, byte[] preallocateBuffer)
  34. : base(www, preallocateBuffer)
  35. {
  36. }
  37. /// <summary>
  38. /// Returns the downloaded Texture, or null.
  39. /// </summary>
  40. public Texture2D Texture
  41. {
  42. get
  43. {
  44. if (m_Texture == null)
  45. {
  46. m_Texture = new Texture2D(1, 1);
  47. m_Texture.LoadImage(GetData(), true);
  48. }
  49. return m_Texture;
  50. }
  51. }
  52. }
  53. /// <summary>
  54. /// Cacheable download handler.
  55. /// </summary>
  56. public abstract class CacheableDownloadHandler : DownloadHandlerScript
  57. {
  58. const string kLog = "[WebRequestCaching] ";
  59. const string kDataSufix = "_d";
  60. const string kEtagSufix = "_e";
  61. static string s_WebCachePath;
  62. static SHA1CryptoServiceProvider s_SHA1 = new SHA1CryptoServiceProvider();
  63. /// <summary>
  64. /// Is the download already finished?
  65. /// </summary>
  66. public new bool isDone { get; private set; }
  67. UnityWebRequest m_WebRequest;
  68. MemoryStream m_Stream;
  69. protected byte[] m_Buffer;
  70. internal CacheableDownloadHandler(UnityWebRequest www, byte[] preallocateBuffer)
  71. : base(preallocateBuffer)
  72. {
  73. this.m_WebRequest = www;
  74. m_Stream = new MemoryStream(preallocateBuffer.Length);
  75. }
  76. /// <summary>
  77. /// Get path for web-caching.
  78. /// </summary>
  79. public static string GetCachePath(string url)
  80. {
  81. if (s_WebCachePath == null)
  82. {
  83. s_WebCachePath = Application.temporaryCachePath + "/WebCache/";
  84. Logger.Debug("{0}WebCachePath : {1}", kLog, s_WebCachePath);
  85. }
  86. if (!Directory.Exists(s_WebCachePath))
  87. Directory.CreateDirectory(s_WebCachePath);
  88. return s_WebCachePath + Convert.ToBase64String(s_SHA1.ComputeHash(UTF8Encoding.Default.GetBytes(url))).Replace('/', '_');
  89. }
  90. /// <summary>
  91. /// Get cached Etag for url.
  92. /// </summary>
  93. public static string GetCacheEtag(string url)
  94. {
  95. var path = GetCachePath(url);
  96. var infoPath = path + kEtagSufix;
  97. var dataPath = path + kDataSufix;
  98. return File.Exists(infoPath) && File.Exists(dataPath)
  99. ? File.ReadAllText(infoPath)
  100. : null;
  101. }
  102. /// <summary>
  103. /// Load cached data for url.
  104. /// </summary>
  105. public static byte[] LoadCache(string url)
  106. {
  107. return File.ReadAllBytes(GetCachePath(url) + kDataSufix);
  108. }
  109. /// <summary>
  110. /// Save cache data for url.
  111. /// </summary>
  112. public static void SaveCache(string url, string etag, byte[] datas)
  113. {
  114. var path = GetCachePath(url);
  115. File.WriteAllText(path + kEtagSufix, etag);
  116. File.WriteAllBytes(path + kDataSufix, datas);
  117. }
  118. /// <summary>
  119. /// Callback, invoked when the data property is accessed.
  120. /// </summary>
  121. protected override byte[] GetData()
  122. {
  123. if (!isDone)
  124. {
  125. Logger.Error("{0}Downloading is not completed : {1}", kLog, m_WebRequest.url);
  126. throw new InvalidOperationException("Downloading is not completed. " + m_WebRequest.url);
  127. }
  128. else if (m_Buffer == null)
  129. {
  130. // Etag cache hit!
  131. if (m_WebRequest.responseCode == 304)
  132. {
  133. Logger.Debug("<color=green>{0}Etag cache hit : {1}</color>", kLog, m_WebRequest.url);
  134. m_Buffer = LoadCache(m_WebRequest.url);
  135. }
  136. // Download is completed successfully.
  137. else if (m_WebRequest.responseCode == 200)
  138. {
  139. Logger.Debug("<color=green>{0}Download is completed successfully : {1}</color>", kLog, m_WebRequest.url);
  140. m_Buffer = m_Stream.GetBuffer();
  141. SaveCache(m_WebRequest.url, m_WebRequest.GetResponseHeader("Etag"), m_Buffer);
  142. }
  143. }
  144. if (m_Stream != null)
  145. {
  146. m_Stream.Dispose();
  147. m_Stream = null;
  148. }
  149. return m_Buffer;
  150. }
  151. /// <summary>
  152. /// Callback, invoked as data is received from the remote server.
  153. /// </summary>
  154. protected override bool ReceiveData(byte[] data, int dataLength)
  155. {
  156. m_Stream.Write(data, 0, dataLength);
  157. return true;
  158. }
  159. /// <summary>
  160. /// Callback, invoked when all data has been received from the remote server.
  161. /// </summary>
  162. protected override void CompleteContent()
  163. {
  164. base.CompleteContent();
  165. isDone = true;
  166. }
  167. /// <summary>
  168. /// Signals that this [DownloadHandler] is no longer being used, and should clean up any resources it is using.
  169. /// </summary>
  170. public new void Dispose()
  171. {
  172. base.Dispose();
  173. if (m_Stream != null)
  174. {
  175. m_Stream.Dispose();
  176. m_Stream = null;
  177. }
  178. }
  179. }
  180. }