各位小老弟,可能已经遇到www协议过时的问题了
但又因为BestHttp是一个插件,在大型公司,不好直接将自己的插件放在项目中。
那么下面,我们就来看看Unity自己的最新网络传输方法:
UnityWebRequest
网络传输一般采用Get、Post、Put、Head,
同时还有一些控制的API,或者得到数据之类的API,那么就先从Get说起
Get可用来下载字符串、数组、图片、AB包、音频等的数据。
当下载完成,可再将数据进行你想要的转换。
void Start()
{
StartCoroutine(SendGetRequest());
}
IEnumerator SendGetRequest()
{
UnityWebRequest uwr = UnityWebRequest.Get("http://www.baidu.com");
yield return uwr.SendWebRequest();
if (uwr.isHttpError || uwr.isNetworkError)
{
Debug.Log(uwr.error);
}
else //请求成功
{
Debug.Log("Get:请求成功");
Debug.Log("下载的数据:" + uwr.downloadedBytes);
}
}
Post可以键值对的形式,将数据存在表单WWWForm里,
来上传字符串、图片、音频等的数据。
void Start()
{
StartCoroutine(SendPostRequest());
}
IEnumerator SendPostRequest()
{
WWWForm form = new WWWForm();
form.AddField("key", "value");
form.AddField("name", "Chinar");
UnityWebRequest webRequest = UnityWebRequest.Post("http://www.baidu.com", form);
yield return webRequest.SendWebRequest();
if (webRequest.isHttpError || webRequest.isNetworkError)
{
Debug.Log(webRequest.error);
}
else
{
Debug.Log("发送成功");
}
}
UnityWebRequest数据传输,除了一般我们用的最多的Post、Get,其实还有Put、Head
Post 和 Put 确实很相像,通俗解释就是-------
新建一条记录的话就用post, 更新一条记录的话就用put. 但你用Post更新数据也没问题。
Put上传的东西跟Post相同,同样可以上传音视频、字符串等数据。
void Start() {
StartCoroutine(SendPutRequest());
}
//更新数据
IEnumerator SendPutRequest() {
byte[] myData = System.Text.Encoding.UTF8.GetBytes("This is some test data");
UnityWebRequest www = UnityWebRequest.Put("http://www.my-server.com/upload", myData);
yield return www.SendWebRequest();
if(www.isNetworkError || www.isHttpError) {
Debug.Log(www.error);
}
else {
Debug.Log("Upload complete!");
}
}
Head跟Get类似,不同的是Head不会下载文件,只会得到头文件数据。
你可以先了解到这个文件的大小等信息,再让用户决定怎样操作。
使用方法:见下方的 “获取要下载数据的长度”。
有些人又要说了,那我会传输方法了,那怎样怎样得到传输速度呢?
本段就开始讲解UnityWebRequest的各个实用的方法,或者API
void Start()
{
StartCoroutine(SendRequest());
}
//测试网址是否连通
IEnumerator SendRequest()
{
Uri uri = new Uri("http://www.baidu.com");
UnityWebRequest uwr = new UnityWebRequest(uri);
//等待时间(秒) 超过此数值时,UnityWebReqest的尝试连接将终止
uwr.timeout = 5;
yield return uwr.SendWebRequest();
if (uwr.isHttpError || uwr.isNetworkError)
{
Debug.LogError("请求失败:" + uwr.error);
}
else //请求成功
{
Debug.Log("请求成功");
}
}
这个API通常用在要下载文件时,先显示要下载文件的大小。
比如《炉石》的是否更新界面、《和平精英》的新场景资源包界面
使用Head的好处是,Head会得到要下载数据的头文件,却不会下载文件。
void Start()
{
StartCoroutine(SendHeadRequest());
}
private IEnumerator SendHeadRequest(string url)
{
UnityWebRequest huwr = UnityWebRequest.Head(url);
yield return huwr.SendWebRequest();
if (huwr.isNetworkError || huwr.isHttpError) //如果出错
{
Debug.Log(huwr.error); //输出 错误信息
}
else
{
long totalLength = long.Parse(huwr.GetResponseHeader("Content-Length"));
print("文件的长度:"+totalLength);
}
}
public void Start()
{
StartCoroutine(DownloadFile());
}
IEnumerator DownloadFile()
{
UnityWebRequest uwr = UnityWebRequest.Get("http://dpv.videocc.net/689be7713e/1/689be7713e7898052861311bd74fc831_3.mp4?pid=1611569020809X1563841");
uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError)
{
Debug.Log(uwr.error);
}
else
{
while (!uwr.isDone)
{
GetComponent<Slider>().value = uwr.downloadProgress;
//Math.floor() 返回小于或等于一个给定数字的最大整数。
GetComponent<Text>().text = Math.Floor(uwr.downloadProgress * 100) + "%";
yield return 0;
}
if (uwr.isDone) //如果下载完成了
{
GetComponent<Slider>().value = 1; //改变Slider的值
GetComponent<Text>().text = 100 + "%";
}
}
}
注意哈,
我们跟上面的Get相比,要注意这儿就没有yield return webRequest.SendWebRequest()了,
而是直接 webRequest.SendWebRequest()
因为yield return后,是直接下载完成或者下载失败。但我们在这儿不需要等待,我们需要时刻知道下载进度。
跟下载进度类似,是将downloadProgress,变成了uploadProgress。
void Start()
{
StartCoroutine(SendPostRequest());
}
IEnumerator SendPostRequest()
{
WWWForm form = new WWWForm();
form.AddField("key", "value");
form.AddField("name", "Chinar");
UnityWebRequest webRequest = UnityWebRequest.Post("http://www.baidu.com", form);
webRequest.SendWebRequest();
if (webRequest.isNetworkError || webRequest.isHttpError)
{
Debug.Log(webRequest.error);
}
else
{
while (!webRequest.isDone)
{
GetComponent<Slider>().value = webRequest.uploadProgress;
//Math.floor() 返回小于或等于一个给定数字的最大整数。
GetComponent<Text>().text = Math.Floor(webRequest.uploadProgress * 100) + "%";
yield return 0;
}
if (webRequest.isDone) //如果下载完成了
{
GetComponent<Slider>().value = 1; //改变Slider的值
GetComponent<Text>().text = 100 + "%";
}
}
}
...
byte[] results = uwr.downloadHandler.data;
// 注意真机上要用Application.persistentDataPath
CreateFile(Application.streamingAssetsPath + "/MP4/test.mp4", results, uwr.downloadHandler.data.Length);
AssetDatabase.Refresh(); //刷新一下
...
void CreateFile(string path, byte[] bytes, int length)
{
FileInfo file = new FileInfo(path);
if (file.Exists)
return;
Stream sw;
sw = file.Create();
sw.Write(bytes, 0, length);
sw.Close();
sw.Dispose();
}
断点续传的机制是通过Head先知道要下载文件的大小,然后Get进行下载。
若点了暂停下载,实际是通过break,取消了下载的StartCoroutine。再次开始下载是再次调用StartCoroutine下载。
但再次下载,会先检测本地文件的长度,然后将该长度传入下载请求,从该长度处后开始下载、写入文件,完成断点续传。
public Button downloadBtn; //开始下载按钮
public Button controBtn; //暂停下载与恢复下载按钮
private string Url = "http://dpv.videocc.net/689be7713e/1/689be7713e7898052861311bd74fc831_3.mp4?pid=1611569020809X1563841";
private bool _isPause;
void Start()
{
downloadBtn.onClick.AddListener(StartDownload);
controBtn.onClick.AddListener(DownLoadCon);
}
public void DownLoadCon()
{
if (_isPause)
{
_isPause = false;
Debug.Log("当前继续开始下载");
StartDownload();
}
else
{
_isPause = true;
Debug.Log("当前已暂停下载");
}
}
private void StartDownload()
{
// 注意真机上要用Application.persistentDataPath
StartCoroutine(DownloadFile(Url, Application.streamingAssetsPath + "/MP4/test.mp4"));
}
private IEnumerator DownloadFile(string url, string filePath)
{
UnityWebRequest huwr = UnityWebRequest.Head(url);
yield return huwr.SendWebRequest();
if (huwr.isNetworkError || huwr.isHttpError)
{
Debug.Log(huwr.error);
}
else
{
long totalLength = long.Parse(huwr.GetResponseHeader("Content-Length"));
print("文件的全部长度:" + totalLength);
string dirPath = Path.GetDirectoryName(filePath);
if (!Directory.Exists(dirPath)) //判断路径是否存在
{
Directory.CreateDirectory(dirPath);
}
//创建一个文件流,指定路径为filePath,模式为打开或创建,访问为写入
using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write))
{
long nowFileLength = fs.Length; //当前本地的文件长度
if (nowFileLength < totalLength)
{
// FileStream.Seek:从当前文件一开始,向后移动nowFileLength个字节,将该流的当前位置设置为给定值。
fs.Seek(nowFileLength, SeekOrigin.Begin);
UnityWebRequest uwr = UnityWebRequest.Get(url); //创建UnityWebRequest对象,将Url传入
uwr.SetRequestHeader("Range", "bytes=" + nowFileLength + "-" + totalLength);
uwr.SendWebRequest();
if (uwr.isNetworkError || uwr.isHttpError)
{
Debug.Log(uwr.error);
}
else
{
long index = 0;
while (nowFileLength < totalLength) //只要下载没有完成,一直执行此循环
{
print(nowFileLength);
if (_isPause) //暂停下载
break;
yield return null;
byte[] data = uwr.downloadHandler.data;
if (data != null)
{
long length = data.Length - index;
fs.Write(data, (int) index, (int) length);
index += length;
nowFileLength += length;
if (nowFileLength >= totalLength)
{
print("下载完成!");
AssetDatabase.Refresh();
break;
}
}
}
}
}
}
}
}
小提示: FileStream.Seek(long offset, SeekOrigin origin):有两个参数。第一参数规定文件指针以字节为单位移动的距离。第二个规定开始计算的位置 SeekOrigin是一个枚举,包含三个值:Begin、Current、End。
大家还有什么问题,欢迎在下方留言!