• [游戏开发][Unity] UnityWebRequest中断续传


     UnityWebRequest和WWW加载的底层原理还是Http

    断点续传的原理

    1. UnityWebRequest第一次请求Url,如果请求成功,从头文件里把文件总长度读出来
      long totalLength = long.Parse(huwr.GetResponseHeader("Content-Length"))
    2. 由于文件是边下边写入的,当开启下载时,可以使用FileStream把已下载的数据长度读出来,因此,当前已经下载了多少个字节是已知的。
      1. using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write))
      2. {
      3. long nowFileLength = fs.Length; //当前文件长度,断点前已经下载的文件长度。
    3. 开启第二次Url请求,把已下载字节长度添加到头文件并开始从断点处再次下载。
      1. UnityWebRequest uwr = UnityWebRequest.Get(url);
      2. uwr.SetRequestHeader("Range", "bytes=" + nowFileLength + "-" + totalLength);
      3. uwr.SendWebRequest();
    4. 继续边下边写,直到文件全部下载完成
      1. byte[] data = uwr.downloadHandler.data;
      2. if (data != null)
      3. {
      4. long length = data.Length - index;
      5. //边下边写
      6. fs.Write(data, (int)index, (int)length);
      7. nowFileLength += length;
    5. 文件下载完成,调用回调方法通知下载完成
      1. //如果下载完成了
      2. if (nowFileLength >= totalLength)
      3. {
      4. callBack?.Invoke();
      5. }

    下面这个代码从网上看到了,我复制过来记录下,提醒一下,如果是在项目里使用,这个Demo的代码还是远远不够的,需要结合自己的项目需求改动一下

    1. using System;
    2. using System.Collections;
    3. using System.IO;
    4. using UnityEngine;
    5. using UnityEngine.Networking;
    6. using UnityEngine.UI;
    7. public class StopBeginDownload : MonoBehaviour
    8. {
    9. public Slider ProgressBar; //进度条
    10. public Text SliderValue; //滑动条值
    11. public Button startBtn; //开始按钮
    12. public Button pauseBtn; //暂停按钮
    13. private bool _isStop; //是否暂停
    14. ///
    15. /// 初始化UI界面及给按钮绑定方法
    16. ///
    17. void Start()
    18. {
    19. //初始化进度条和文本框
    20. ProgressBar.value = 0;
    21. SliderValue.text = "0.0%";
    22. //开始、暂停按钮事件监听
    23. startBtn.onClick.AddListener(OnClickStartDownload);
    24. pauseBtn.onClick.AddListener(OnClickStop);
    25. }
    26. //开始下载按钮监听事件
    27. public void OnClickStartDownload()
    28. {
    29. //开启协程 *注意真机上要用Application.persistentDataPath路径*
    30. string urlStr = "https://gitcode.net/liuyongjie1992/galaxy/-/raw/master/audioclips/cv/1.unity3d";
    31. StartCoroutine(DownloadFile(urlStr, Application.streamingAssetsPath + "/1.unity3d", CallBack));
    32. }
    33. ///
    34. /// 协程:下载文件
    35. ///
    36. /// 请求的Web地址
    37. /// 文件保存路径
    38. /// 下载完成的回调函数
    39. ///
    40. IEnumerator DownloadFile(string url, string filePath, Action callBack)
    41. {
    42. UnityWebRequest huwr = UnityWebRequest.Head(url); //使用Head方法可以获取到文件的全部长度
    43. yield return huwr.SendWebRequest();//发送信息请求
    44. //判断请求或系统是否出错
    45. if (huwr.isNetworkError || huwr.isHttpError)
    46. {
    47. Debug.Log(huwr.error); //出现错误 输出错误信息
    48. }
    49. else
    50. {
    51. Debug.Log("第一次request,成功");
    52. long totalLength = long.Parse(huwr.GetResponseHeader("Content-Length")); //首先拿到文件的全部长度
    53. string dirPath = Path.GetDirectoryName(filePath);//获取文件的上一级目录
    54. if (!Directory.Exists(dirPath)) //判断路径是否存在
    55. {
    56. Directory.CreateDirectory(dirPath);//不存在创建
    57. }
    58. /*作用:创建一个文件流,指定路径为filePath,模式为打开或创建,访问为写入
    59. * 使用using(){}方法原因: 当同一个cs引用了不同的命名空间,但这些命名控件都包括了一个相同名字的类型的时候,可以使用using关键字来创建别名,这样会使代码更简洁。注意:并不是说两个名字重复,给其中一个用了别名,另外一个就不需要用别名了,如果两个都要使用,则两个都需要用using来定义别名的
    60. * using(类){} 括号中的类必须是继承了IDisposable接口才能使用否则报错
    61. * 这里没有出现不同命名空间出现相同名字的类属性可以不用using(){}
    62. */
    63. using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write))
    64. {
    65. long nowFileLength = fs.Length; //当前文件长度,断点前已经下载的文件长度。
    66. //判断当前文件是否小于要下载文件的长度,即文件是否下载完成
    67. if (nowFileLength < totalLength)
    68. {
    69. Debug.Log("开始继续下载,已下载长度: " + nowFileLength);
    70. /*使用Seek方法 可以随机读写文件
    71. * Seek() ----------有两个参数 第一参数规定文件指针以字节为单位移动的距离。第二个参数规定开始计算的位置
    72. * 第二个参数SeekOrigin 有三个值:Begin Current End
    73. * fs.Seek(8,SeekOrigin.Begin);表示 将文件指针从开头位置移动到文件的第8个字节
    74. * fs.Seek(8,SeekOrigin.Current);表示 将文件指针从当前位置移动到文件的第8个字节
    75. * fs.Seek(8,SeekOrigin.End);表示 将文件指针从最后位置移动到文件的第8个字节
    76. */
    77. fs.Seek(nowFileLength, SeekOrigin.Begin); //从开头位置,移动到当前已下载的子节位置
    78. UnityWebRequest uwr = UnityWebRequest.Get(url); //创建UnityWebRequest对象,将Url传入
    79. uwr.SetRequestHeader("Range", "bytes=" + nowFileLength + "-" + totalLength);//修改请求头从n-m之间
    80. uwr.SendWebRequest();//开始请求
    81. if (uwr.isNetworkError || uwr.isHttpError) //如果出错
    82. {
    83. Debug.Log(uwr.error); //输出 错误信息
    84. }
    85. else
    86. {
    87. long index = 0; //从该索引处继续下载
    88. while (nowFileLength < totalLength) //只要下载没有完成,一直执行此循环
    89. {
    90. if (_isStop) break;//如果停止跳出循环
    91. yield return null;
    92. byte[] data = uwr.downloadHandler.data;
    93. if (data != null)
    94. {
    95. long length = data.Length - index;
    96. fs.Write(data, (int)index, (int)length); //写入文件
    97. index += length;
    98. nowFileLength += length;
    99. ProgressBar.value = (float)nowFileLength / totalLength;
    100. SliderValue.text = Math.Floor((float)nowFileLength / totalLength * 100) + "%";
    101. if (nowFileLength >= totalLength) //如果下载完成了
    102. {
    103. ProgressBar.value = 1; //改变Slider的值
    104. SliderValue.text = 100 + "%";
    105. /*这句话的作用是:如果callBack方法不为空则执行Invoke
    106. * 注意:
    107. * 1.这里的Invoke可不是Unity的Invoke延迟调用的用法,参考文章:https://blog.csdn.net/liujiejieliu1234/article/details/45312141 从文章中我们可以看到,C#中的Invoke是为了防止winform中子主线程刚开始创建对象时,子线程与主线程并发修改主线程尚未创建的对象属性。
    108. * 因为unity这里只有主线程没有用到子线程可以直接写callBack();
    109. */
    110. SliderValue.text = "下载完成,执行回调";
    111. callBack?.Invoke();
    112. break;
    113. }
    114. }
    115. }
    116. }
    117. }
    118. else
    119. {
    120. Debug.Log("文件已完整存在,不能继续下载");
    121. }
    122. }
    123. }
    124. }
    125. ///
    126. /// 下载完成后的回调函数
    127. ///
    128. void CallBack()
    129. {
    130. Debug.Log("下载回调完成");
    131. }
    132. ///
    133. /// 暂停下载
    134. ///
    135. public void OnClickStop()
    136. {
    137. if (_isStop)
    138. {
    139. pauseBtn.GetComponentInChildren().text = "暂停下载";
    140. Debug.Log("继续下载");
    141. _isStop = !_isStop;
    142. OnClickStartDownload();
    143. }
    144. else
    145. {
    146. pauseBtn.GetComponentInChildren().text = "继续下载";
    147. Debug.Log("暂停下载");
    148. _isStop = !_isStop;
    149. }
    150. }
    151. }

  • 相关阅读:
    pyqt5将ui文件转为python文件
    产品经理常用工具汇总
    Java强弱软虚4种引用
    CoDeSys系列-4、基于Ubuntu的codesys运行时扩展包搭建Profinet主从环境
    Spring 域对象共享数据
    多年锤炼,迈向Kata 3.0 !走进开箱即用的安全容器体验之旅
    Sparksql补齐连续工单状态
    Cesium快速上手9-Camera和Scene中的其他函数使用
    【解决方案】SkeyeVSS综合安防视频技术保障游乐场安全嗨玩体验
    Cloudera Manager-6.2.0安装文档
  • 原文地址:https://blog.csdn.net/liuyongjie1992/article/details/133805224