57.网络通信-Unity网络类-UnityWebRequest类-高级上传数据
57.1 知识点
回顾高级操作中的获取数据
主要做法
将二进制字节数组处理,独立到下载处理对象中进行处理,主要设置 UnityWebRequest
对象中 downloadHandler
变量。
Unity 内置的类
DownloadHandlerBuffer
:用于简单的数据存储,获取对应的二进制数据。DownloadHandlerFile
:用于下载文件并将文件保存到磁盘(内存占用较少)。DownloadHandlerTexture
:用于下载图像。DownloadHandlerAssetBundle
:用于提取 AssetBundle。DownloadHandlerAudioClip
:用于下载音频文件。
自定义拓展方式
- 继承
DownloadHandlerScript
类,并重写其中的固定方法,自定义处理字节数组。
自定义上传数据 UploadHandler 相关类
注意:由于 UnityWebRequest
类在常用操作中已经封装了上传数据相关内容,我们可以方便地上传参数和文件,满足常用需求。以下内容主要用于了解,特别重要的变量是 contentType
内容类型,如果不设置,默认是 application/octet-stream
二进制流的形式。
UploadHandlerRaw上传字节数组
// 用于上传字节数组的协程
IEnumerator UploadHandlerRaw()
{
// 创建一个 UnityWebRequest 对象,指定要访问的 URL 和 HTTP 请求方法为 POST
UnityWebRequest unityWebRequest = new UnityWebRequest("http://192.168.1.101:8000/HTTPRoot/pic.png", UnityWebRequest.kHttpVerbPOST);
// 准备要上传的字节数组
byte[] bytes = Encoding.UTF8.GetBytes("123123123123123");
// 将字节数组分配给 UnityWebRequest 的 uploadHandler,用于上传数据
unityWebRequest.uploadHandler = new UploadHandlerRaw(bytes);
// 如果需要指定上传数据的类型,可以设置 contentType 属性
// unityWebRequest.uploadHandler.contentType = "类型/细分类型";
// 发送网络请求,并等待请求返回
yield return unityWebRequest.SendWebRequest();
// 打印请求的结果(成功或失败)
print(unityWebRequest.result);
}
StartCoroutine(UploadHandlerRaw());
UploadHandlerFile上传文件
// 用于上传文件的协程
IEnumerator UploadHandlerFile()
{
// 创建一个 UnityWebRequest 对象,指定要访问的 URL 和 HTTP 请求方法为 POST
UnityWebRequest unityWebRequest = new UnityWebRequest("http://192.168.1.101:8000/HTTPRoot/pic.png", UnityWebRequest.kHttpVerbPOST);
// 创建一个用于上传文件的 UploadHandlerFile 对象,指定要上传的文件路径
unityWebRequest.uploadHandler = new UploadHandlerFile(Application.streamingAssetsPath + "/test.png");
// 发送网络请求,并等待请求返回
yield return unityWebRequest.SendWebRequest();
// 打印请求的结果(成功或失败)
print(unityWebRequest.result);
}
StartCoroutine(UploadHandlerFile());
运行结果
总结
- 由于
UnityWebRequest
已经提供了较为完善的参数上传、文件上传相关功能,因此高级操作中的上传数据相关内容拓展较少,使用也较少。我们使用常用操作的上传数据相关功能已经足够,高级操作中的上传数据知识点主要做了解。
57.2 知识点代码
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
public class Lesson57_网络通信_Unity网络类_UnityWebRequest类_高级上传数据 : MonoBehaviour
{
void Start()
{
#region 知识点一 回顾高级操作中的获取数据
//主要做法:将2进制字节数组处理,独立到下载处理对象中进行处理
// 主要就是设置UnityWebRequest对象中 downloadHandler 变量
//Unity写好的类有
//1.DownloadHandlerBuffer 用于简单的数据存储,得到对应的2进制数据。
//2.DownloadHandlerFile 用于下载文件并将文件保存到磁盘(内存占用少)。
//3.DownloadHandlerTexture 用于下载图像。
//4.DownloadHandlerAssetBundle 用于提取 AssetBundle。
//5.DownloadHandlerAudioClip 用于下载音频文件。
//自己拓展处理方式
//继承 DownloadHandlerScript
//并重写其中的固定方法,自己处理字节数组
#endregion
#region 知识点二 自定义上传数据UploadHandler相关类
//注意:
//由于UnityWebRequest类的常用操作中
//上传数据相关内容已经封装的很好了
//我们可以很方便的上传参数和文件
//我们使用常用操作已经能够满足常用需求了
//所以以下内容主要做了解
//UploadHandler相关类
//1.UploadHandlerRaw 用于上传字节数组
StartCoroutine(UploadHandlerRaw());
//2.UploadHandlerFile 用于上传文件
StartCoroutine(UploadHandlerFile());
//其中比较重要的变量是
//contentType 内容类型,如果不设置,模式是 application/octet-stream 2进制流的形式
#endregion
#region 总结
//由于上传数据相关 UnityWebRequest原本已经提供了较为完善的
//参数上传、文件上传相关功能
//所以高级操作中的 上传数据相关内容拓展较少,使用也较少
//我们使用常用操作的上传数据相关功能就足够了
//高级操作的上传数据知识点主要做了解
#endregion
}
#region 知识点二 自定义上传数据UploadHandler相关类
// 用于上传字节数组的协程
IEnumerator UploadHandlerRaw()
{
// 创建一个UnityWebRequest对象,指定要访问的URL和HTTP请求方法为POST
UnityWebRequest unityWebRequest = new UnityWebRequest("http://192.168.1.101:8000/HTTPRoot/pic.png", UnityWebRequest.kHttpVerbPOST);
// 准备要上传的字节数组
byte[] bytes = Encoding.UTF8.GetBytes("123123123123123");
// 将字节数组分配给UnityWebRequest的uploadHandler,用于上传数据
unityWebRequest.uploadHandler = new UploadHandlerRaw(bytes);
// 如果需要指定上传数据的类型,可以设置contentType属性
// unityWebRequest.uploadHandler.contentType = "类型/细分类型";
// 发送网络请求,并等待请求返回
yield return unityWebRequest.SendWebRequest();
// 打印请求的结果(成功或失败)
print(unityWebRequest.result);
}
// 用于上传文件的协程
IEnumerator UploadHandlerFile()
{
// 创建一个UnityWebRequest对象,指定要访问的URL和HTTP请求方法为POST
UnityWebRequest unityWebRequest = new UnityWebRequest("http://192.168.1.101:8000/HTTPRoot/pic.png", UnityWebRequest.kHttpVerbPOST);
// 创建一个用于上传文件的UploadHandlerFile对象,指定要上传的文件路径
unityWebRequest.uploadHandler = new UploadHandlerFile(Application.streamingAssetsPath + "/test.png");
// 发送网络请求,并等待请求返回
yield return unityWebRequest.SendWebRequest();
// 打印请求的结果(成功或失败)
print(unityWebRequest.result);
}
#endregion
}
57.3 练习题
请在一个NetWWW管理类当中,封装一个基于UnityWebRequest下载远程数据或加载本地数据的方法,加载完成后,通过委托的形式让外部使用
在定义协程UnityWebRequest去获取数据方法和启动协程方法
/// <summary>
/// 通过UnityWebRequest去获取数据
/// </summary>
/// <typeparam name="T">byte[]、Texture、AssetBundle、AudioClip、object(自定义的 如果是object证明要保存到本地)</typeparam>
/// <param name="path">远端或者本地数据路径 http ftp file</param>
/// <param name="action">获取成功后的回调函数</param>
/// <param name="localPath">如果是下载到本地 需要传第3个参数</param>
/// <param name="type">如果是下载 音效切片文件 需要穿音效类型</param>
public void UnityWebRequestLoad<T>(string path, UnityAction<T> action, string localPath = "", AudioType type = AudioType.MPEG) where T : class
{
StartCoroutine(UnityWebRequestLoadAsync<T>(path, action, localPath, type));
}
/// <summary>
/// 异步方法,使用 UnityWebRequest 获取数据。
/// </summary>
/// <typeparam name="T">返回数据的类型,可以是 byte[]、Texture、AssetBundle、AudioClip、object(自定义类型,用于本地保存)</typeparam>
/// <param name="path">远程或本地数据的路径,可以是 http、ftp、file 协议</param>
/// <param name="action">获取成功后的回调函数</param>
/// <param name="localPath">如果是下载到本地,需要传递本地文件保存路径</param>
/// <param name="type">如果是下载音效文件,需要指定音效类型(默认为 MPEG)</param>
/// <returns>IEnumerator,用于协程处理</returns>
private IEnumerator UnityWebRequestLoadAsync<T>(string path, UnityAction<T> action, string localPath = "", AudioType type = AudioType.MPEG) where T : class
{
// 创建 UnityWebRequest 实例,指定请求方式为 GET,并传递目标路径。
UnityWebRequest unityWebRequest = new UnityWebRequest(path, UnityWebRequest.kHttpVerbGET);
// 根据泛型类型 T 设置不同的下载处理器。
if (typeof(T) == typeof(byte[]))
unityWebRequest.downloadHandler = new DownloadHandlerBuffer();
else if (typeof(T) == typeof(Texture))
unityWebRequest.downloadHandler = new DownloadHandlerTexture();
else if (typeof(T) == typeof(AssetBundle))
unityWebRequest.downloadHandler = new DownloadHandlerAssetBundle(unityWebRequest.url, 0);
else if (typeof(T) == typeof(object))
unityWebRequest.downloadHandler = new DownloadHandlerFile(localPath);
else if (typeof(T) == typeof(AudioClip))
unityWebRequest = UnityWebRequestMultimedia.GetAudioClip(path, type);
else
{
// 如果泛型类型不在预期的类型中,记录警告并退出协程。
Debug.LogWarning("未知类型" + typeof(T));
yield break;
}
// 发送 UnityWebRequest 并等待请求完成。
yield return unityWebRequest.SendWebRequest();
if (unityWebRequest.result == UnityWebRequest.Result.Success)
{
// 根据泛型类型 T 处理不同的下载结果,并调用回调函数 action。
if (typeof(T) == typeof(byte[]))
action?.Invoke(unityWebRequest.downloadHandler.data as T);
else if (typeof(T) == typeof(Texture))
action?.Invoke(DownloadHandlerTexture.GetContent(unityWebRequest) as T);
else if (typeof(T) == typeof(AssetBundle))
action?.Invoke((unityWebRequest.downloadHandler as DownloadHandlerAssetBundle).assetBundle as T);
else if (typeof(T) == typeof(object))
action?.Invoke(null);
else if (typeof(T) == typeof(AudioClip))
action?.Invoke(DownloadHandlerAudioClip.GetContent(unityWebRequest) as T);
}
else
{
// 如果请求失败,记录错误信息。
Debug.LogWarning("获取数据失败" + unityWebRequest.result + unityWebRequest.error + unityWebRequest.responseCode);
}
}
进行测试
NetWWWManager.Instance.UnityWebRequestLoad<Texture>("http://192.168.1.101:8000/HTTPRoot/pic.png", (texture) =>
{
rawImage.texture = texture;
Debug.Log("Texture下载成功");
});
NetWWWManager.Instance.UnityWebRequestLoad<byte[]>("http://192.168.1.101:8000/HTTPRoot/pic.png", (bytes) =>
{
Debug.Log("字节数组长度为:" + bytes.Length);
});
NetWWWManager.Instance.UnityWebRequestLoad<object>("http://192.168.1.101:8000/HTTPRoot/pic.png", (obj) =>
{
Debug.Log("保存本地成功");
},Application.persistentDataPath+ "/UnityWebRequestLoad.png");
Debug.Log(Application.persistentDataPath);
运行结果
57.4 练习题代码
NetWWWManager
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
public class NetWWWManager : BaseSingletonInMonoBehaviour<NetWWWManager>
{
private string HTTP_SERVER_PATH = "http://192.168.1.101:8000/HTTPRoot/";
/// <summary>
/// 提供给外部加载资源用的方法
/// </summary>
/// <typeparam name="T">资源的类型</typeparam>
/// <param name="path">资源的路径 http ftp file都支持</param>
/// <param name="action">加载结束后的回调函数 因为WWW是通过结合协同程序异步加载的 所以不能马上获取结果 需要回调获取</param>
public void LoadResource<T>(string path, UnityAction<T> action) where T : class
{
StartCoroutine(LoadResourceAsync<T>(path, action));
}
/// <summary>
/// 内部异步使用WWW加载资源用的协程方法
/// </summary>
/// <typeparam name="T">资源的类型</typeparam>
/// <param name="path">资源的路径 http ftp file都支持</param>
/// <param name="action">加载结束后的回调函数 因为WWW是通过结合协同程序异步加载的 所以不能马上获取结果 需要回调获取</param>
private IEnumerator LoadResourceAsync<T>(string path, UnityAction<T> action) where T : class
{
//声明www对象 用于下载或加载
WWW www = new WWW(path);
//等待下载或者加载结束(异步)
yield return www;
//优化的话可以让外部多传一个在加载时逻辑处理的委托回调
//如果没有错误 证明加载成功
if (www.error == null)
{
//根据T泛型的类型 决定使用哪种类型的资源 传递给外部
if (typeof(T) == typeof(AssetBundle))
{
action?.Invoke(www.assetBundle as T);
}
else if (typeof(T) == typeof(Texture))
{
action?.Invoke(www.texture as T);
}
else if (typeof(T) == typeof(AudioClip))
{
action?.Invoke(www.GetAudioClip() as T);
}
else if (typeof(T) == typeof(string))
{
action?.Invoke(www.text as T);
}
else if (typeof(T) == typeof(byte[]))
{
action?.Invoke(www.bytes as T);
}
//自定义一些类型 可能需要将bytes 转换成对应的类型来使用
}
//如果错误 就提示别人
else
{
//优化的话可以让外部多传一个在错误时委托回调
Debug.LogError("www加载资源出错" + www.error + "path:" + path);
}
}
/// <summary>
/// 发送消息的通用方法,通过协程异步发送消息并在接收响应后执行指定的动作。
/// </summary>
/// <typeparam name="T">消息类型的泛型参数,必须是BaseMsg的派生类。</typeparam>
/// <param name="baseMessage">要发送的消息对象。</param>
/// <param name="action">消息发送成功后要执行的动作。</param>
public void SendMessage<T>(BaseMessage baseMessage, UnityAction<T> action) where T : BaseMessage
{
// 启动协程异步发送消息
StartCoroutine(SendMessageAsync<T>(baseMessage, action));
}
/// <summary>
/// 通过协程异步发送消息,并等待响应。
/// </summary>
/// <typeparam name="T">消息类型的泛型参数,必须是BaseMsg的派生类。</typeparam>
/// <param name="BaseMessage">要发送的消息对象。</param>
/// <param name="action">消息发送成功后要执行的动作。</param>
private IEnumerator SendMessageAsync<T>(BaseMessage BaseMessage, UnityAction<T> action) where T : BaseMessage
{
// 创建一个WWWForm来准备要发送的消息数据
WWWForm wWWForm = new WWWForm();
// 将消息数据以二进制形式添加到WWWForm中
wWWForm.AddBinaryData("BaseMessage", BaseMessage.Writing());
// 创建一个WWW对象并发送消息到HTTP服务器
WWW www = new WWW(HTTP_SERVER_PATH, wWWForm);
// 等待异步操作完成,即等待消息发送结束
yield return www;
// 检查是否有错误发生
if (www.error == null)
{
// 解析从服务器返回的消息,提取消息ID和消息长度
int index = 0;
int msgID = BitConverter.ToInt32(www.bytes, index);
index += 4;
int msgLength = BitConverter.ToInt32(www.bytes, index);
index += 4;
// 根据消息ID反序列化消息
BaseMessage baseMessage = null;
switch (msgID)
{
case 1001:
baseMessage = new PlayerMessage();
baseMessage.Reading(www.bytes, index);
break;
}
// 调用指定的动作,并将反序列化后的消息作为泛型参数传递
if (baseMessage != null)
action?.Invoke(baseMessage as T);
}
else
{
// 输出错误信息
Debug.LogError("发消息出问题" + www.error);
}
}
/// <summary>
/// 上传文件的方法
/// </summary>
/// <param name="fileName">上传上去的文件名</param>
/// <param name="localPath">本地想要上传文件的路径</param>
/// <param name="action">上传完成后的回调函数</param>
public void UploadFile(string fileName, string localPath, UnityAction<UnityWebRequest.Result> action)
{
StartCoroutine(UploadFileAsync(fileName, localPath, action));
}
/// <summary>
/// 异步上传文件的方法
/// </summary>
/// <param name="fileName">上传上去的文件名</param>
/// <param name="localPath">本地想要上传文件的路径</param>
/// <param name="action">上传完成后的回调函数</param>
/// <returns></returns>
private IEnumerator UploadFileAsync(string fileName, string localPath, UnityAction<UnityWebRequest.Result> action)
{
// 创建一个列表来存储上传文件的数据
List<IMultipartFormSection> multipartFormSectionList = new List<IMultipartFormSection>();
// 向列表中添加要上传的文件,包括文件名和文件的二进制数据
multipartFormSectionList.Add(new MultipartFormFileSection(fileName, File.ReadAllBytes(localPath)));
// 创建一个UnityWebRequest对象,用于执行文件上传
UnityWebRequest unityWebRequest = UnityWebRequest.Post(HTTP_SERVER_PATH, multipartFormSectionList);
// 发送上传请求并等待完成
yield return unityWebRequest.SendWebRequest();
// 调用回调函数,传递上传结果
action?.Invoke(unityWebRequest.result);
// 如果上传不成功
if (unityWebRequest.result != UnityWebRequest.Result.Success)
{
// 输出警告信息,包括错误消息和响应代码
Debug.LogWarning("上传出现问题" + unityWebRequest.error + unityWebRequest.responseCode);
}
}
/// <summary>
/// 通过UnityWebRequest去获取数据
/// </summary>
/// <typeparam name="T">byte[]、Texture、AssetBundle、AudioClip、object(自定义的 如果是object证明要保存到本地)</typeparam>
/// <param name="path">远端或者本地数据路径 http ftp file</param>
/// <param name="action">获取成功后的回调函数</param>
/// <param name="localPath">如果是下载到本地 需要传第3个参数</param>
/// <param name="type">如果是下载 音效切片文件 需要穿音效类型</param>
public void UnityWebRequestLoad<T>(string path, UnityAction<T> action, string localPath = "", AudioType type = AudioType.MPEG) where T : class
{
StartCoroutine(UnityWebRequestLoadAsync<T>(path, action, localPath, type));
}
/// <summary>
/// 异步方法,使用 UnityWebRequest 获取数据。
/// </summary>
/// <typeparam name="T">返回数据的类型,可以是 byte[]、Texture、AssetBundle、AudioClip、object(自定义类型,用于本地保存)</typeparam>
/// <param name="path">远程或本地数据的路径,可以是 http、ftp、file 协议</param>
/// <param name="action">获取成功后的回调函数</param>
/// <param name="localPath">如果是下载到本地,需要传递本地文件保存路径</param>
/// <param name="type">如果是下载音效文件,需要指定音效类型(默认为 MPEG)</param>
/// <returns>IEnumerator,用于协程处理</returns>
private IEnumerator UnityWebRequestLoadAsync<T>(string path, UnityAction<T> action, string localPath = "", AudioType type = AudioType.MPEG) where T : class
{
// 创建 UnityWebRequest 实例,指定请求方式为 GET,并传递目标路径。
UnityWebRequest unityWebRequest = new UnityWebRequest(path, UnityWebRequest.kHttpVerbGET);
// 根据泛型类型 T 设置不同的下载处理器。
if (typeof(T) == typeof(byte[]))
unityWebRequest.downloadHandler = new DownloadHandlerBuffer();
else if (typeof(T) == typeof(Texture))
unityWebRequest.downloadHandler = new DownloadHandlerTexture();
else if (typeof(T) == typeof(AssetBundle))
unityWebRequest.downloadHandler = new DownloadHandlerAssetBundle(unityWebRequest.url, 0);
else if (typeof(T) == typeof(object))
unityWebRequest.downloadHandler = new DownloadHandlerFile(localPath);
else if (typeof(T) == typeof(AudioClip))
unityWebRequest = UnityWebRequestMultimedia.GetAudioClip(path, type);
else
{
// 如果泛型类型不在预期的类型中,记录警告并退出协程。
Debug.LogWarning("未知类型" + typeof(T));
yield break;
}
// 发送 UnityWebRequest 并等待请求完成。
yield return unityWebRequest.SendWebRequest();
if (unityWebRequest.result == UnityWebRequest.Result.Success)
{
// 根据泛型类型 T 处理不同的下载结果,并调用回调函数 action。
if (typeof(T) == typeof(byte[]))
action?.Invoke(unityWebRequest.downloadHandler.data as T);
else if (typeof(T) == typeof(Texture))
action?.Invoke(DownloadHandlerTexture.GetContent(unityWebRequest) as T);
else if (typeof(T) == typeof(AssetBundle))
action?.Invoke((unityWebRequest.downloadHandler as DownloadHandlerAssetBundle).assetBundle as T);
else if (typeof(T) == typeof(object))
action?.Invoke(null);
else if (typeof(T) == typeof(AudioClip))
action?.Invoke(DownloadHandlerAudioClip.GetContent(unityWebRequest) as T);
}
else
{
// 如果请求失败,记录错误信息。
Debug.LogWarning("获取数据失败" + unityWebRequest.result + unityWebRequest.error + unityWebRequest.responseCode);
}
}
}
Lesson57_练习题
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class Lesson57_练习题 : MonoBehaviour
{
public RawImage rawImage;
void Start()
{
NetWWWManager.Instance.UnityWebRequestLoad<Texture>("http://192.168.1.101:8000/HTTPRoot/pic.png", (texture) =>
{
rawImage.texture = texture;
Debug.Log("Texture下载成功");
});
NetWWWManager.Instance.UnityWebRequestLoad<byte[]>("http://192.168.1.101:8000/HTTPRoot/pic.png", (bytes) =>
{
Debug.Log("字节数组长度为:" + bytes.Length);
});
NetWWWManager.Instance.UnityWebRequestLoad<object>("http://192.168.1.101:8000/HTTPRoot/pic.png", (obj) =>
{
Debug.Log("保存本地成功");
},Application.persistentDataPath+ "/UnityWebRequestLoad.png");
Debug.Log(Application.persistentDataPath);
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com