最近有个项目需要在服务器下载视频资源到本地,然后在本地播放视频,一开始看Unity官方文档找到资源下载的方法,但是在下载超大资源(一个2.8G的.mp4格式的视频)的时候回出现未知错误导致视频不能下载下来,先看官方提供的方法如下。
?
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
public class FileDownloader : MonoBehaviour {
void Start () {
StartCoroutine(DownloadFile());
}
IEnumerator DownloadFile() {
var uwr = new UnityWebRequest("https://unity3d.com/", UnityWebRequest.kHttpVerbGET);
string path = Path.Combine(Application.persistentDataPath, "unity3d.html");
uwr.downloadHandler = new DownloadHandlerFile(path);
yield return uwr.SendWebRequest();
if (uwr.result != UnityWebRequest.Result.Success)
Debug.LogError(uwr.error);
else
Debug.Log("File successfully downloaded and saved to " + path);
}
}
于是我们采用断点续传和分段下载来实现超大资源的下载。
断点续传
户端软件断点续传指的是在下载或上传时,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载。用户可以节省时间,提高速度。
思路
获取到资源的总长度,获取到当前已经下载的长度,当下载中断再次请求下载的时候,先判断有没有下载完成,如果没有下载完成,从当前位置开始,下载剩下的资源。
GetResponseHeader("Content-Length")获取到资源的大小,下图中name=ueno&age=37正好是16个字节。这样我们就能获取到资源的总字节数。
SetRequestHeader("Range", "bytes=" + fileLength + "-")请求网络数据从第fileLength到最后的字节;
完整代码
代码注释已经比较详细
using System.Collections;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
public class FileDownloader : MonoBehaviour
{
public Text text;
private string MyURL = "http://education.jnaw.top/lubozhibouploaded/quanjing/1615791915263.mp4";
//private string MyURL = "http://education.jnaw.top/lubozhibouploaded/quanjing/1629796505018.mp4";
//十亿字节为一段
double loadedBytes = 1000000000;
void Start()
{
Debug.Log(Application.persistentDataPath);
StartCoroutine(BreakpointResume(MyURL, Application.persistentDataPath + "/MP4/1615791915263.mp4"));
}
/// <summary>
/// 分段,断点下载文件
/// </summary>
/// <param name="loadPath">下载地址</param>
/// <param name="savePath">保存路径</param>
/// <returns></returns>
IEnumerator BreakpointResume(string loadPath, string savePath)
{
//UnityWebRequest 经配置可传输 HTTP HEAD 请求的 UnityWebRequest。
UnityWebRequest headRequest = UnityWebRequest.Head(loadPath);
//开始与远程服务器通信。
yield return headRequest.SendWebRequest();
if (!string.IsNullOrEmpty(headRequest.error))
{
Debug.LogError("获取不到资源文件");
yield break;
}
//获取文件总大小
ulong totalLength = ulong.Parse(headRequest.GetResponseHeader("Content-Length"));
Debug.Log("获取大小" + totalLength);
headRequest.Dispose();
UnityWebRequest Request = UnityWebRequest.Get(loadPath);
//append设置为true文件写入方式为接续写入,不覆盖原文件。
Request.downloadHandler = new DownloadHandlerFile(savePath, true);
//创建文件
FileInfo file = new FileInfo(savePath);
//当前下载的文件长度
ulong fileLength = (ulong)file.Length;
//请求网络数据从第fileLength到最后的字节;
Request.SetRequestHeader("Range", "bytes=" + fileLength + "-");
if (!string.IsNullOrEmpty(Request.error))
{
Debug.LogError("下载失败" + Request.error);
}
if (fileLength < totalLength)
{
Request.SendWebRequest();
while (!Request.isDone)
{
double progress = (Request.downloadedBytes + fileLength) / (double)totalLength;
text.text = (progress * 100 + 0.01f).ToString("f2") + "%";
// Debug.Log("下载量" + Request.downloadedBytes);
//超过一定的字节关闭现在的协程,开启新的协程,将资源分段下载
if (Request.downloadedBytes >= loadedBytes)
{
StopCoroutine("BreakpointResume");
//如果 UnityWebRequest 在进行中,就停止。
Request.Abort();
if (!string.IsNullOrEmpty(Request.error))
{
Debug.LogError("下载失败" + Request.error);
}
yield return StartCoroutine(BreakpointResume(loadPath, savePath));
}
yield return null;
}
}
if (string.IsNullOrEmpty(Request.error))
{
Debug.Log("下载成功" + savePath);
text.text = "100%";
}
//表示不再使用此 UnityWebRequest,并且应清理它使用的所有资源。
Request.Dispose();
}
}
|