2025-05-11 Unity 网络基础11——UnityWebRequest

本文环境

  • Windows 11
  • Unity 6000.0.42f1

1 UnityWebRequest 介绍

​ UnityWebRequest 是 Unity 提供的模块化的系统类,用于构成 HTTP 请求和处理 HTTP 响应。

​ 它主要目标是让 Unity 游戏和 Web 服务端进行交互,将之前 WWW 的相关功能都集成在了其中,新版本中都建议使用 UnityWebRequest 类代替 WWW 类。

​ UnityWebRequest 在使用上和 WWW 很类似,主要区别是 UnityWebRequest 把下载下来的数据处理单独提取出来,可以根据自己的需求选择对应的数据处理对象来获取数据。

注意

  1. UnityWebRequest 和 WWW 一样,需要配合协同程序使用。
  2. UnityWebRequest 和 WWW 一样,支持 http、ftp、file 协议下载或加载资源。
  3. UnityWebRequest 能够上传文件到 HTTP 资源服务器。

常用操作

  1. 使用 Get 请求获取文本或二进制数据。
  2. 使用 Get 请求获取纹理数据。
  3. 使用 Get 请求获取 AB 包数据。
  4. 使用 Post 请求发送数据。
  5. 使用 Put 请求上传数据。

2 搭建 HTTP 服务器

​ Unity 默认不允许不安全的连接(如 HTTP),如果服务器是本地开发服务器,可以启用 Unity 的 Allow downloads over HTTP 选项(位于 Edit > Project Settings > Player > Other Settings)。

image-20250511112546176

3 常用操作

3.1下载资源

3.1.1 下载文本

  • 获取文本或二进制数据时

    使用 UnityWebRequest.Get()

using UnityEngine;

namespace Lesson
{
    using System.Collections;
    using UnityEngine.Networking;

    public class Lesson30 : MonoBehaviour
    {
        // 在Start方法中启动LoadText协程
        private void Start()
        {
            StartCoroutine(LoadText());
        }

        // 定义LoadText协程
        IEnumerator LoadText()
        {
            // 发送GET请求
            var req = UnityWebRequest.Get("http://你的 IP 地址:8000/HTTP Server/test.txt");
            
            // 等待请求完成
            yield return req.SendWebRequest();

            // 如果请求成功
            if (req.result == UnityWebRequest.Result.Success)
            {
                // 打印下载的文本
                print(req.downloadHandler.text);
                
                // 获取下载的字节数组
                var bytes = req.downloadHandler.data;
                
                // 打印字节数组的长度
                print("字节数组长度: " + bytes.Length);
            }
            // 如果请求失败
            else
            {
                // 打印错误信息
                print($"获取失败: {req.error} | {req.result} | {req.responseCode}");
            }
        }
    }
}

​ 将脚本挂载到 Unity 场景上并运行,得到以下结果。

image-20250511112629771

3.1.2 下载图片

  • 获取纹理图片数据时

    使用 UnityWebRequestTexture.GetTexture()

    以及 DownloadHandlerTexture.GetContent()

    该方法也支持

    • ftp 下载。
    • file 本地文件下载。
using UnityEngine;

namespace Lesson
{
    using System.Collections;
    using UnityEngine.Networking;
    using UnityEngine.UI;

    public class Lesson30 : MonoBehaviour
    {
        // 声明一个RawImage类型的变量Img
        public RawImage Img;

        // 在Start方法中启动LoadText协程
        private void Start()
        {
            StartCoroutine(LoadTexture());
        }

        IEnumerator LoadTexture()
        {
            // 创建一个UnityWebRequestTexture对象,用于获取指定URL的纹理
            var req = UnityWebRequestTexture.GetTexture("http://你的 IP 地址:8000/HTTP Server/test.png");

            // var req = UnityWebRequestTexture.GetTexture("ftp://你的 IP 地址:8000/HTTP Server/test.png");

            // var req = UnityWebRequestTexture.GetTexture("file://" + Application.streamingAssetsPath + "/test.png");

            // 发送网络请求
            yield return req.SendWebRequest();

            // 如果请求成功
            if (req.result == UnityWebRequest.Result.Success)
            {
                // 方法一
                // Img.texture = (req.downloadHandler as DownloadHandlerTexture).texture;

                // 方法二
                // 获取下载的纹理
                Img.texture = DownloadHandlerTexture.GetContent(req);
            }
            else
            {
                // 打印错误信息
                print($"获取失败: {req.error} | {req.result} | {req.responseCode}");
            }
        }
    }
}

​ 将脚本挂载到 Unity 场景上,并在 Unity 场景中创建一个 RawImage 关联至 Img。

​ 运行 Unity,RawImage 上出现了从服务器下载的图片。

image-20250511114140792

3.1.3 下载 AB 包

  • 获取 AB 包数据时

    使用 UnityWebRequestAssetBundle.GetAssetBundle()

    以及 DownloadHandlerAssetBundle.GetContent()

using UnityEngine;

namespace Lesson
{
    using System.Collections;
    using UnityEngine.Networking;
    using UnityEngine.UI;

    public class Lesson30 : MonoBehaviour
    {
        // 在Start方法中启动LoadText协程
        private void Start()
        {
            StartCoroutine(LoadAB());
        }

        IEnumerator LoadAB()
        {
            // 创建一个UnityWebRequest对象,用于获取AssetBundle
            UnityWebRequest req = UnityWebRequestAssetBundle.GetAssetBundle("http://你的 IP 地址:8000/HTTP Server/ab2");

            // 发送请求
            req.SendWebRequest();

            // 循环等待请求完成
            while (!req.isDone)
            {
                // 打印下载进度
                print(req.downloadProgress);

                // 打印已下载的字节数
                print(req.downloadedBytes);

                // 等待一帧
                yield return null;
            }

            //yield return req.SendWebRequest();

            // 打印下载进度
            print(req.downloadProgress);

            // 打印已下载的字节数
            print(req.downloadedBytes);

            // 如果请求成功
            if (req.result == UnityWebRequest.Result.Success)
            {
                // 获取AssetBundle对象
                // AssetBundle ab = (req.downloadHandler as DownloadHandlerAssetBundle).assetBundle;

                // 获取AssetBundle对象
                var ab = DownloadHandlerAssetBundle.GetContent(req);

                // 打印AssetBundle的名称
                print(ab.name);
            }
            else
            {
                // 打印错误信息
                print($"获取失败: {req.error} | {req.result} | {req.responseCode}");
            }
        }
    }
}

3.2 上传资源

3.2.1 上传数据类

父接口:IMultipartFormSection

  • 所有上传数据类的父接口
  • 可以使用父类容器存储子类对象
var dataList = new List<IMultipartFormSection>();

子类1:MultipartFormDataSection

  • 用于上传普通数据
// 1. 二进制字节数组
dataList.Add(new MultipartFormDataSection(Encoding.UTF8.GetBytes("123123123123123")));

// 2. 字符串
dataList.Add(new MultipartFormDataSection("12312312312312312dsfasdf"));

// 3. 带参数名和编码类型(常用)
dataList.Add(new MultipartFormDataSection("Name", "xxx", Encoding.UTF8, "application/...."));

// 4. 带参数名的字节数组
dataList.Add(new MultipartFormDataSection("Msg", new byte[1024], "appl....."));

子类2:MultipartFormFileSection

  • 用于上传文件数据
// 1. 简单字节数组
dataList.Add(new MultipartFormFileSection(File.ReadAllBytes(Application.streamingAssetsPath + "/test.png")));

// 2. 带文件名的字节数组(常用)
dataList.Add(new MultipartFormFileSection("上传的文件.png", File.ReadAllBytes(Application.streamingAssetsPath + "/test.png")));

// 3. 字符串数据+文件名(常用)
dataList.Add(new MultipartFormFileSection("12312313212312", "test.txt"));

// 4. 带编码格式的字符串数据+文件名(常用)
dataList.Add(new MultipartFormFileSection("12312313212312", Encoding.UTF8, "test.txt"));

// 5. 完整参数:表单名+字节数组+文件名+文件类型
dataList.Add(new MultipartFormFileSection("file", new byte[1024], "test.txt", ""));

// 6. 完整参数:表单名+字符串数据+编码格式+文件名
dataList.Add(new MultipartFormFileSection("file", "123123123", Encoding.UTF8, "test.txt"));

3.2.2 POST 上传

基本流程

  1. 准备上传数据列表。
  2. 创建 UnityWebRequest.POST 请求。
  3. 发送请求并监控进度。
  4. 处理响应结果。
using UnityEngine;

namespace Lesson
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using UnityEngine.Networking;
    using UnityEngine.UI;

    public class Lesson31 : MonoBehaviour
    {
        // 在Start方法中启动LoadText协程
        private void Start()
        {
            StartCoroutine(Upload());
        }

        IEnumerator Upload()
        {
            // 创建一个包含要上传的数据的列表
            var data = new List<IMultipartFormSection>
            {
                // 添加一个名为Name的表单字段,值为xxx
                new MultipartFormDataSection("Name", "xxx"),

                // 添加一个名为png的文件字段,值为test.png文件的内容
                new MultipartFormFileSection("png", File.ReadAllBytes(Application.streamingAssetsPath + "/test.png")),

                // 添加一个名为txt的文本字段,值为12312321231232123
                new MultipartFormFileSection("txt", "12312321231232123"),
            };

            // 创建一个POST请求,将数据发送到指定的URL
            var req = UnityWebRequest.Post("http://你的 IP 地址:8000/HTTP Server/", data);

            // 发送请求
            req.SendWebRequest();

            // 循环等待请求完成
            while (!req.isDone)
            {
                // 打印上传进度
                print(req.uploadProgress);

                // 打印已上传的字节数
                print(req.uploadedBytes);

                // 等待一帧
                yield return null;
            }

            // 打印上传进度
            print(req.uploadProgress);

            // 打印已上传的字节数
            print(req.uploadedBytes);

            // 如果请求成功
            if (req.result == UnityWebRequest.Result.Success)
            {
                // 打印上传成功
                print("上传成功");

                // req.downloadHandler.data
            }
            else
            {
                // 打印获取失败的信息
                print($"获取失败: {req.error} | {req.result} | {req.responseCode}");
            }
        }
    }
}

​ 将脚本挂载到 Unity 场景上并运行,得到以下结果。

image-20250511121305306

​ 在服务器文件夹中,可看到上传了 2 个文件(注意要确认可以匿名访问服务器,才可上传成功)。

image-20250511121803853

3.3.3 PUT 上传

  • 不是所有服务器都支持 PUT 方法。
  • 需要服务器端明确支持 PUT 请求处理。
IEnumerator UploadPut()
{
    var req = UnityWebRequest.Put("http://你的 IP 地址:8000/HTTP Server/", 
                                  File.ReadAllBytes(Application.streamingAssetsPath + "/test.png"));

    yield return req.SendWebRequest();

    if (req.result == UnityWebRequest.Result.Success)
    {
        print("Put 上传成功");
    }
    else
    { }
}

4 自定义操作

​ 自定义作指使用 UnityWebRequest 提供的底层功能来处理常规封装方法无法满足的需求,核心思想是将数据处理与网络传输分离。通过自定义 DownloadHandler 和 UploadHandler,实现更灵活的数据获取和上传功能。

相关 API

// 1.构造函数
UnityWebRequest req = new UnityWebRequest();

// 2.请求地址
req.url = "服务器地址";

// 3.请求类型
req.method = UnityWebRequest.kHttpVerbPOST;

// 4.进度
req.downloadProgress
req.uploadProgress

// 5.超时设置
req.timeout = 2000;

// 6.上传、下载的字节数
req.downloadedBytes
req.uploadedBytes

// 7.重定向次数 设置为0表示不进行重定向 可以设置次数
req.redirectLimit = 10;

// 8.状态码、结果、错误内容
req.result
req.error
req.responseCode

// 9.下载、上传处理对象
req.downloadHandler
req.uploadHandler

4.1 下载资源

4.1.1 Unity 内置 Handler

处理器类型用途示例获取数据方式特点
DownloadHandlerBuffer原始二进制数据new DownloadHandlerBuffer()handler.data最基础的处理方式
DownloadHandlerFile直接保存到文件new DownloadHandlerFile(path)自动保存到指定路径内存占用少,适合大文件
DownloadHandlerTexture图片下载new DownloadHandlerTexture()handler.texture自动转换为 Texture2D
DownloadHandlerAssetBundleAB 包下载new DownloadHandlerAssetBundle(url, crc)handler.assetBundle支持 CRC 校验
DownloadHandlerAudioClip音频下载UnityWebRequestMultimedia.GetAudioClip()DownloadHandlerAudioClip.GetContent(req)需要指定音频类型

​ DownloadHandler 的使用基本遵循以下模式:

  1. 创建 UnityWebRequest 对象:指定 URL 和请求方法。
  2. 创建特定类型的 DownloadHandler:根据要下载的数据类型选择。
  3. 将 DownloadHandler 赋给请求对象:req.downloadHandler = handler
  4. 发送请求:yield return req.SendWebRequest()
  5. 处理结果:检查req.result,成功后通过 handler 获取数据。

DownLoadTexture 示例

IEnumerator DownLoadTexture()
{
    // 创建一个UnityWebRequest对象,用于发送HTTP请求
    var req = new UnityWebRequest("http://你的 IP 地址:8000/Http Server/test.png",
                                  UnityWebRequest.kHttpVerbGET);

    // 创建一个DownloadHandlerTexture对象,用于处理下载的纹理
    var textureHandler = new DownloadHandlerTexture();

    // 将DownloadHandlerTexture对象赋值给UnityWebRequest对象的downloadHandler属性
    req.downloadHandler = textureHandler;

    // 发送HTTP请求
    yield return req.SendWebRequest();

    // 如果请求成功
    if (req.result == UnityWebRequest.Result.Success)
    {
        // 将下载的纹理赋值给Image对象的texture属性
        Image.texture = textureHandler.texture;
    }
    else
    {
        // 打印错误信息
        print("获取数据失败" + req.result + req.error + req.responseCode);
    }
}

DownLoadAB 示例

IEnumerator DownLoadAB()
{
    // 创建一个UnityWebRequest对象,用于发送HTTP请求
    var req = new UnityWebRequest("http://你的 IP 地址:8000/Http Server/ab", UnityWebRequest.kHttpVerbGET);

    // 创建一个DownloadHandlerAssetBundle对象,用于处理下载的AssetBundle
    // 第二个参数需要已知校检码才能进行比较,检查完整性。如果不知道的话只能传0,不进行完整性的检查
    // 所以一般只有进行AB包热更新时,服务器发送对应的文件列表中包含了验证码才能进行检查
    var handler = new DownloadHandlerAssetBundle(req.url, 0);

    // 将DownloadHandlerAssetBundle对象赋值给UnityWebRequest对象的downloadHandler属性
    req.downloadHandler = handler;

    // 发送HTTP请求
    yield return req.SendWebRequest();

    // 如果请求成功
    if (req.result == UnityWebRequest.Result.Success)
    {
        // 获取下载的AssetBundle
        var ab = handler.assetBundle;

        // 打印AssetBundle的名称
        print(ab.name);
    }

    // 如果请求失败
    else
    {
        // 打印错误信息
        print("获取数据失败" + req.result + req.error + req.responseCode);
    }
}

DownLoadAudioClip 示例

IEnumerator DownLoadAudioClip()
{
    // 创建一个UnityWebRequestMultimedia对象,用于获取音频文件
    var req = UnityWebRequestMultimedia.GetAudioClip("http://你的 IP 地址:8000/Http Server/Night Changes.mp3", AudioType.MPEG);

    // 发送网络请求
    yield return req.SendWebRequest();

    // 如果请求成功
    if (req.result == UnityWebRequest.Result.Success)
    {
        // 获取音频文件内容
        var a = DownloadHandlerAudioClip.GetContent(req);
    }

    // 如果请求失败
    else
    {
        // 打印错误信息
        print("获取数据失败" + req.result + req.error + req.responseCode);
    }
}

​ DownLoadAudioClip 中,由于创建时不是 new UnityWebRequest(),而是指定了特殊的创建方法。因此不设置 DownloadHandlerAudioClip,而是通过 DownloadHandlerAudioClip 获得音频文件。

4.1.2 自定义 Handler

​ 以上这些类是 Unity 中实现的用于解析下载下来的数据的类,使用对应的类处理下载数据,他们就会在内部将下载的数据处理为对应的类型。

DownloadHandlerScript 是一个特殊类,本身而言不会执行任何操作。但此类可由用户定义的类继承,接收来自 UnityWebRequest 系统的回调,然后使用这些回调在数据从网络到达时执行完全自定义的数据处理。

自定义下载器

public class CustomDownLoadFileHandler : DownloadHandlerScript
{
    // 用于保存本地存储时的路径
    private string _savePath;

    // 用于缓存收到的数据的容器
    private byte[] _cacheBytes;

    // 这是当前已收到的数据长度
    private int _index = 0;

    public CustomDownLoadFileHandler() : base()
    { }

    public CustomDownLoadFileHandler(byte[] bytes) : base(bytes)
    { }

    public CustomDownLoadFileHandler(string path) : base()
    {
        _savePath = path;
    }

    protected override byte[] GetData()
    {
        // 返回字节数组
        return _cacheBytes;
    }

    /// <summary>
    /// 从网络收到数据后,每帧会自动调用的方法
    /// </summary>
    protected override bool ReceiveData(byte[] data, int dataLength)
    {
        Debug.Log("收到数据长度:" + data.Length);
        Debug.Log("收到数据长度dataLength:" + dataLength);
        data.CopyTo(_cacheBytes, _index);
        _index += dataLength;
        return true;
    }

    /// <summary>
    /// 从服务器收到 Content-Length标头时,会自动调用的方法
    /// </summary>
    protected override void ReceiveContentLengthHeader(ulong contentLength)
    {
        // base.ReceiveContentLengthHeader(contentLength);
        Debug.Log("收到数据长度:" + contentLength);

        // 根据收到的标头,决定字节数组容器的大小
        _cacheBytes = new byte[contentLength];
    }

    /// <summary>
    /// 当消息收完了会自动调用的方法
    /// </summary>
    protected override void CompleteContent()
    {
        Debug.Log("消息收完");

        // 把收到的字节数组进行自定义处理,我们在这处理成存储到本地
        File.WriteAllBytes(_savePath, _cacheBytes);
    }
}

使用自定义下载器

using UnityEngine;

namespace Lesson
{
    using System.Collections;
    using System.IO;
    using UnityEngine.Networking;
    using UnityEngine.UI;

    public class Lesson32 : MonoBehaviour
    {
        // 在Start方法中启动LoadText协程
        private void Start()
        {
            StartCoroutine(DownLoadCustomHandler());
        }

        IEnumerator DownLoadCustomHandler()
        {
            // 创建一个UnityWebRequest对象,用于发送HTTP请求
            var req = new UnityWebRequest("http://你的 IP 地址:8000/Http Server/Night Changes.mp3", UnityWebRequest.kHttpVerbGET);

            // 使用自定义的下载处理对象来处理获取到的2进制字节数组
            req.downloadHandler = new CustomDownLoadFileHandler(Application.streamingAssetsPath + "/Night Changes.mp3");

            // 发送HTTP请求
            yield return req.SendWebRequest();

            // 判断请求是否成功
            if (req.result == UnityWebRequest.Result.Success)
            {
                // 打印成功信息
                print("存储本地成功");
            }
            else
            {
                // 打印失败信息
                print("获取数据失败" + req.result + req.error + req.responseCode);
            }
        }

        public class CustomDownLoadFileHandler : DownloadHandlerScript
        {
            ...
        }
    }
}

4.2 上传资源

​ UnityWebRequest 类的常用操作中,上传数据相关内容已经封装的很好了,可以很方便的上传参数和文件,因此使用常用操作已经能够满足常用需求,以下内容主要做了解。

​ UnityWebRequest 提供了两种主要的上传数据处理方式:

  1. UploadHandlerRaw - 用于上传字节数组数据。
  2. UploadHandlerFile - 用于上传文件数据。

​ 这些上传处理器会自动处理数据转换和传输过程,只需提供原始数据即可。

4.2.1 UploadHandlerRaw

  • 功能:上传原始字节数组数据。
  • 特点
    • 适合上传结构化数据如 JSON、XML 等。
    • 需要手动设置 contentType。
    • 数据会被复制到 native 内存中。
IEnumerator UpLoadRaw()
{
    // 创建一个UnityWebRequest对象,指定请求的URL和请求方法
    var req = new UnityWebRequest("http://你的 IP 地址:8000/Http Server/", UnityWebRequest.kHttpVerbPOST);

    // 将字符串转换为字节数组
    byte[] bytes = Encoding.UTF8.GetBytes("123123123123123");

    // 设置上传处理程序,将字节数组作为上传数据
    req.uploadHandler = new UploadHandlerRaw(bytes);

    // 设置上传处理程序的Content-Type为application/octet-stream
    req.uploadHandler.contentType = "application/octet-stream";

    // 发送请求,并等待响应
    yield return req.SendWebRequest();

    // 打印请求结果
    print(req.result);
}

4.2.2 UploadHandlerFile

  • 功能:直接上传文件内容。
  • 特点
    • 适合上传大文件,内存占用低。
    • 文件会分块读取和上传。
    • 默认 contentType 为 application/octet-stream
IEnumerator UpLoadFile()
{
    // 创建一个UnityWebRequest对象,指定请求的URL和请求方法
    var req = new UnityWebRequest("http://你的 IP 地址:8000/Http Server/", UnityWebRequest.kHttpVerbPOST);

    // 设置上传文件的路径
    req.uploadHandler = new UploadHandlerFile(Application.streamingAssetsPath + "/test.png");

    // 发送请求并等待响应
    yield return req.SendWebRequest();

    // 打印请求结果
    print(req.result);
}

​ UploadHandler 会自动处理 contentType,优先级如下:

  1. 如果在 UnityWebRequest 上手动设置了 Content-Type 头,则使用该值。
  2. 如果在 UploadHandler 上设置了 contentType,则使用该值。
  3. 默认使用 application/octet-stream

常见 contentType 值

  • application/json - JSON数据
  • application/xml - XML数据
  • image/png - PNG图片
  • application/octet-stream - 二进制流(默认)

注意事项

  1. UnityWebRequest 默认会在 Dispose 时自动释放 uploadHandler。

  2. 如果需要保留 uploadHandler 引用,需设置

    req.disposeUploadHandlerOnDispose = false;
    
  3. 使用 using 语句确保资源释放。

    using(UnityWebRequest req = new UnityWebRequest(...))
    {
        // 使用请求
    }
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蔗理苦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值