🟥 常用网络传输方法
网络传输一般采用Get、Post、Put、Head,
同时还有一些控制的API,或者得到数据之类的API,那么就先从Get说起
1️⃣ 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); } }
2️⃣ Post上传数据
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("发送成功"); } }
3️⃣ Put上传数据
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!"); } }
4️⃣ Head下载头文件
Head跟Get类似,不同的是Head不会下载文件,只会得到头文件数据。
你可以先了解到这个文件的大小等信息,再让用户决定怎样操作。
使用方法:见下方的 “获取要下载数据的长度”。
🟧 网络传输的API
有些人又要说了,那我会传输方法了,那怎样怎样得到传输速度呢?
本段就开始讲解UnityWebRequest的各个实用的方法,或者API
1️⃣ 测试网址是否连通
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("请求成功"); } }
2️⃣ 获取要下载数据的长度
这个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); } }
3️⃣ 显示下载进度
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后,是直接下载完成或者下载失败。但我们在这儿不需要等待,我们需要时刻知道下载进度。
4️⃣ 显示上传进度
跟下载进度类似,是将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 + "%"; } } }
5️⃣ 保存下载的文件
... 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(); }
6️⃣ 断点续传
断点续传的机制是通过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。
- aFile.Seek(8,SeekOrigin.Begin)---------将文件的指针移动到第八个字节。起始位置就是文件的第一个字节
- aFile.Seek(-5,SeekOrigin.End); 从文件的末尾向前查找五个字节
- aFile.Seek(2,SeekOrigin.Current);