57.UnityWebReq类高级上传数据

57.网络通信-Unity网络类-UnityWebRequest类-高级上传数据


57.1 知识点

回顾高级操作中的获取数据

主要做法

将二进制字节数组处理,独立到下载处理对象中进行处理,主要设置 UnityWebRequest 对象中 downloadHandler 变量。

Unity 内置的类

  1. DownloadHandlerBuffer:用于简单的数据存储,获取对应的二进制数据。
  2. DownloadHandlerFile:用于下载文件并将文件保存到磁盘(内存占用较少)。
  3. DownloadHandlerTexture:用于下载图像。
  4. DownloadHandlerAssetBundle:用于提取 AssetBundle。
  5. 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

×

喜欢就点赞,疼爱就打赏