DownStreamWithThreadSample.cs 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. using System.Threading.Tasks;
  2. using Best.HTTP.Caching;
  3. using Best.HTTP.Examples.Helpers;
  4. using Best.HTTP.Response;
  5. using Best.HTTP.Shared;
  6. using Best.HTTP.Shared.Compression.Crc;
  7. using Best.HTTP.Shared.PlatformSupport.Memory;
  8. using UnityEngine;
  9. namespace Best.HTTP.Examples
  10. {
  11. /// <summary>
  12. /// Example showing the usage of download streaming using the DownloadContentStream class.
  13. /// In this example content processing is done on a Thread to make it as fast as possible
  14. /// without causing CPU spikes on the Unity main thread.
  15. /// </summary>
  16. class DownStreamWithThreadSample : SampleBase
  17. {
  18. /// <summary>
  19. /// Precomputed and expected value of the content _baseAddress points to.
  20. /// </summary>
  21. const int EXPECTED_CRC = 0x4B282398;
  22. #pragma warning disable 0649, 0169
  23. [Header("Sample Fields")]
  24. /// <summary>
  25. /// GameObject that will be used as a root for new UI objects.
  26. /// </summary>
  27. [SerializeField]
  28. private RectTransform _contentRoot;
  29. /// <summary>
  30. /// Prefab of a UI object with a Text field.
  31. /// </summary>
  32. [SerializeField]
  33. private TextListItem _listItemPrefab;
  34. #pragma warning restore
  35. /// <summary>
  36. /// Address of the used end point.
  37. /// </summary>
  38. private string _baseAddress = "https://besthttpwebgldemo.azurewebsites.net/test100mb.dat";
  39. /// <summary>
  40. /// Cached reference to the HTTPRequest instance.
  41. /// </summary>
  42. private HTTPRequest _request;
  43. protected override void Start()
  44. {
  45. base.Start();
  46. // Set a custom size to make sure the local cache will accept the downloaded file into its cache.
  47. HTTPManager.LocalCache?.Dispose();
  48. HTTPManager.LocalCache = new HTTPCacheBuilder()
  49. .WithOptions(new HTTPCacheOptionsBuilder()
  50. .WithMaxCacheSize(128 * 1024 * 1024)
  51. .Build())
  52. .Build();
  53. // Create a regular get request with a regular callback too. We still need a callback,
  54. // because it might encounter an error before able to start a download.
  55. _request = HTTPRequest.CreateGet(this._baseAddress, OnRequestFinishedCallack);
  56. // Request a notification when download starts
  57. _request.DownloadSettings.OnDownloadStarted += OnDownloadStarted;
  58. // When needed, create a BlockingDownloadContentStream instead of the regular DownloadContentStream.
  59. // BlockingDownloadContentStream's Take function will block until new data is available.
  60. _request.DownloadSettings.DownloadStreamFactory = (req, resp, bufferAvailableHandler)
  61. => new BlockingDownloadContentStream(resp, req.DownloadSettings.ContentStreamMaxBuffered, bufferAvailableHandler);
  62. _request.DownloadSettings.OnDownloadProgress += OnDownloadProgress;
  63. // Don't want to retry when there's a failure
  64. _request.RetrySettings.MaxRetries = 0;
  65. // Start processing the request
  66. _request.Send();
  67. CreateUIText("Connecting...");
  68. }
  69. private void OnDownloadProgress(HTTPRequest req, long progress, long length)
  70. {
  71. var progressUIEntry = req.Tag as TextListItem;
  72. if (progressUIEntry == null)
  73. req.Tag = progressUIEntry = CreateUIText(string.Empty);
  74. progressUIEntry.SetText($"{progress:N0}/{length:N0}");
  75. }
  76. private async void OnDownloadStarted(HTTPRequest req, HTTPResponse resp, DownloadContentStream stream)
  77. {
  78. CreateUIText("Download started!");
  79. // Task.Run will execute the ConsumeDownloadStream on a background thread.
  80. // By using a thread we can offload the CPU intensive work and
  81. // it's also desirable because it can block the executing thread if the stream is empty!
  82. // Task returns with the calculated checksum.
  83. var crc = await Task.Run<int>(() => ConsumeDownloadStream(stream as BlockingDownloadContentStream));
  84. CreateUIText($"CRC checksum calculation finished. Result: 0x{crc:X}, Expected: 0x{EXPECTED_CRC:X}");
  85. }
  86. int ConsumeDownloadStream(BlockingDownloadContentStream blockingStream)
  87. {
  88. var crc = new CRC32();
  89. try
  90. {
  91. while (!blockingStream.IsCompleted)
  92. {
  93. // Take out a segment from the downloaded
  94. if (blockingStream.TryTake(out var buffer))
  95. {
  96. try
  97. {
  98. // In this case content processing is just calculating the CRC checksum of the data.
  99. crc.SlurpBlock(buffer.Data, buffer.Offset, buffer.Count);
  100. }
  101. finally
  102. {
  103. BufferPool.Release(buffer);
  104. }
  105. }
  106. }
  107. }
  108. finally
  109. {
  110. blockingStream.Dispose();
  111. }
  112. return crc.Crc32Result;
  113. }
  114. private void OnRequestFinishedCallack(HTTPRequest req, HTTPResponse resp)
  115. {
  116. // If we leaved the sample, the _request is nulled out and we can ignore this callback.
  117. if (_request == null)
  118. return;
  119. _request = null;
  120. string log = null;
  121. if (req.State == HTTPRequestStates.Finished)
  122. {
  123. if (resp.IsSuccess)
  124. {
  125. log = "Done! ";
  126. // If IsFromCache is true, the response is read from the local cache.
  127. if (resp.IsFromCache)
  128. log += "From Local Cache!";
  129. else
  130. log += "Fresh From The Server";
  131. }
  132. else
  133. log = resp.StatusCode.ToString();
  134. }
  135. else
  136. log = req.State.ToString();
  137. CreateUIText(log);
  138. }
  139. private void OnDestroy()
  140. {
  141. _request?.Abort();
  142. _request = null;
  143. }
  144. TextListItem CreateUIText(string text)
  145. => Instantiate<TextListItem>(this._listItemPrefab, this._contentRoot)
  146. .SetText(text);
  147. }
  148. }