UN4L8——UnityWebRequest类

本章代码关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
UnityWebRequest                                    //Unity提供的一个模块化的系统类,用于构成HTTP请求和处理HTTP响应,ftp和本地资源也可以用
//创建获取请求相关
UnityWebRequest.Get() //获取文本或者二进制数据的网络请求
UnityWebRequestTexture.GetTexture() //创建获取纹理的网络请求
UnityWebRequestAssetBundle.GetAssetBundle() //创建获取AB包的网络请求
//网络请求执行和状态相关
unityWebRequest.SendWebRequest() //用于yield return 挂起协程直到请求获取内容下载完毕
unityWebRequest.isDone //是否下载/上传完毕
unityWebRequest.downloadedBytes //当前下载的字节数
unityWebRequest.downloadProgress //当前下载的进度(0~1)
unityWebRequest.result //网络请求结果
UnityWebRequest.Result.Success //请求成功
unityWebRequest.error //出错原因
unityWebRequest.responseCode //响应码
//获取下载内容相关
unityWebRequest.downloadHandler.text //下载完毕后获取文本信息
unityWebRequest.downloadHandler.data //下载完毕后获取二进制数据信息
DownloadHandlerTexture.GetContent() //下载完毕后获取纹理消息
DownloadHandlerAssetBundle.GetContent() //下载完毕后获取AB包消息
//上传多数据相关
IMultipartFormSection //UnityWebRequest上传涉及到的数据相关类都继承该接口
MultipartFormDataSection //用于上传键值对数据的数据类,值可以是字符串也可以是字节数组
MultipartFormFileSection //用于上传文件数据的数据类,可以直接上传文本文档或文件的二进制数据,它上传的文件不需要后端处理
//上传相关
UnityWebRequest.Post() //Post创建上传数据请求
UnityWebRequest.Put() //Put创建上传数据请求
unityWebRequest.uploadedBytes //当前上传的字节数
unityWebRequest.uploadProgress //当前上传的进度(0~1)

UnityWebRequest

UnityWebRequest​ 是一个Unity提供的一个模块化的系统类,用于构成HTTP请求和处理HTTP响应
它主要目标是让Unity游戏和Web服务端进行交互,它将之前 WWW​ 的相关功能都集成在了其中
所以新版本中都建议使用 UnityWebRequest​ 类来代替 WWW​ 类,它在使用上和 WWW​ 很类似

WWW​ 主要的区别就是 UnityWebRequest​ 把下载下来的数据处理单独提取出来了,我们可以根据自己的需求选择对应的数据处理对象来获取数据

我们可以利用 Post​ 上传数据或上传文件,Put​ 同样主要用于上传文件,但是必须资源服务器支持 Put​ 请求类型
为了通用性,我们可以统一使用 Post​ 请求类型进行数据和资源的上传,它的使用和之前的 WWW​ 类似,只要前后端制定好规则就可以相互通信了

UnityWebRequest​ 的上传与下载还可以将数据处理分离开,按照自己的规则处理数据,请见:UN4L9——UnityWebRequest高级操作

注意:

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

UnityWebRequest类的常用操作

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

获取相关API

  • Get​ 创建获取文本或者二进制数据的网络请求

    1
    UnityWebRequest unityWebRequest = UnityWebRequest.Get("http://192.168.43.144/Http_Server/test.txt");
  • Get​ 创建获取纹理的网络请求

    1
    UnityWebRequest unityWebRequest = UnityWebRequestTexture.GetTexture("ftp://192.168.43.144/下载测试.png");
  • Get​ 创建获取AB包的网络请求

    1
    UnityWebRequest unityWebRequest = UnityWebRequestAssetBundle.GetAssetBundle("http://192.168.43.144/Http_Server/lua");
  • 发送网络请求

    发送网络请求,执行下载逻辑,在网络获取的协程内,如果 yield return unityWebRequest.SendWebRequest()​,即可让协程挂起到下载完毕

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    IEnumerator LoadText()
    {
    UnityWebRequest unityWebRequest = UnityWebRequest.Get("http://192.168.43.144/Http_Server/test.txt");
    yield return unityWebRequest.SendWebRequest();
    //如果处理成功,结果就是成功的枚举
    if (unityWebRequest.result == UnityWebRequest.Result.Success)
    {
    print(unityWebRequest.downloadHandler.text);
    }
    }
  • 是否下载/上传完毕

    1
    if (!unityWebRequest.isDone) { }
  • 当前下载的字节数

    1
    print($"下载了{unityWebRequest.downloadedBytes}字节");
  • 当前下载的进度(0~1)

    1
    print($"下载到{unityWebRequest.downloadProgress * 100}%");
  • 网络请求结果

    当下载完毕后,就可以通过 unityWebRequest.result​ 判断下载是否成功,如果等于 UnityWebRequest.Result.Success​ 就是下载成功

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    if (unityWebRequest.result == UnityWebRequest.Result.Success)
    {
    print(unityWebRequest.downloadHandler.text);
    byte[] bytes = unityWebRequest.downloadHandler.data;
    print("字节数组长度:" + bytes.Length);
    }
    else
    {
    print("获取失败:" + unityWebRequest.result + unityWebRequest.error + unityWebRequest.responseCode);
    }
  • 请求失败时输出原因

    如果执行失败,可通过 unityWebRequest.result​、unityWebRequest.error​、unityWebRequest.responseCode​,检查出错原因

    其中 unityWebRequest.responseCode​ 是状态码

    1
    print("获取失败:" + unityWebRequest.result + unityWebRequest.error + unityWebRequest.responseCode);
  • 下载完毕后获取文本信息

    1
    print(unityWebRequest.downloadHandler.text);
  • 下载完毕后获取二进制数据信息

    1
    2
    byte[] bytes = unityWebRequest.downloadHandler.data;
    print("字节数组长度:" + bytes.Length);
  • 下载完毕后获取纹理消息

    1
    image.texture = DownloadHandlerTexture.GetContent(unityWebRequest);
  • 下载完毕后获取AB包消息

    1
    2
    AssetBundle ab = DownloadHandlerAssetBundle.GetContent(unityWebRequest);
    print(ab.name);

Get获取操作

UnityWebRequest​ 使用上和 WWW​ 类很类似,我们需要注意的是:

  1. 获取文本或二进制数据时:使用 UnityWebRequest.Get
  2. 获取纹理图片数据时:使用 UnityWebRequestTexture.GetTexture​ 以及 DownloadHandlerTexture.GetContent
  3. 获取AB包数据时:使用 UnityWebRequestAssetBundle.GetAssetBundle​ 以及 DownloadHandlerAssetBundle.GetContent

获取文本和二进制

如果是读取二进制数据或者文本文件内的文本,
需要使用 UnityWebRequest.Get()​ 传入http、ftp、或本地文件地址(file开头)创建一个 unityWebRequest​ 对象
然后 yield return unityWebRequest.SendWebRequest()​,发送网络请求后挂起协程

待处理完毕,回到协程,即可使用 unityWebRequest.result​ 检测执行结果,如果为 UnityWebRequest.Result.Success​ 就是执行成功

如果执行成功,文本数据可通过 unityWebRequest.downloadHandler.text​ 获取字符串内容数据
二进制数据可通过 unityWebRequest.downloadHandler.data​ 获取字节数组形式数据

如果执行失败,可通过 unityWebRequest.result​、unityWebRequest.error​、unityWebRequest.responseCode​,检查出错原因

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void Start()
{
StartCoroutine(LoadText());
}

IEnumerator LoadText()
{
UnityWebRequest unityWebRequest = UnityWebRequest.Get("http://192.168.43.144/Http_Server/test.txt");
//等待服务器端响应后,断开连接后,再继续执行后面的内容
yield return unityWebRequest.SendWebRequest();
//如果处理成功,结果就是成功的枚举
if (unityWebRequest.result == UnityWebRequest.Result.Success)
{
print(unityWebRequest.downloadHandler.text);
byte[] bytes = unityWebRequest.downloadHandler.data;
print("字节数组长度:" + bytes.Length);
}
else
{
print("获取失败:" + unityWebRequest.result + unityWebRequest.error + unityWebRequest.responseCode);
}
}

获取纹理

纹理(图片)的获取使用 UnityWebRequestTexture.GetTexture()​ 方法传入地址创建一个 unityWebRequest​ 对象,即可挂起协程等待获取完毕

获取完毕后,使用 (unityWebRequest.downloadHandler as DownloadHandlerTexture).texture​ 通过转换获取图片
但是更方便的获取方法是,调用 DownloadHandlerTexture.GetContent()​ 方法,传入 unityWebRequest​ 对象,即可获取图片

注:请不要在同一帧里连续去获取同样的资源,尤其是该资源比较大时,会报503的错误(虽然应该不会傻了去同时获取两次一样的资源)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public RawImage image;

void Start()
{
StartCoroutine(LoadTexture());
}

IEnumerator LoadTexture()
{
//UnityWebRequest unityWebRequest = UnityWebRequestTexture.GetTexture("http://192.168.43.144/Http_Server/下载测试.png");
//UnityWebRequest unityWebRequest = UnityWebRequestTexture.GetTexture("ftp://192.168.43.144/下载测试.png");
UnityWebRequest unityWebRequest = UnityWebRequestTexture.GetTexture("file://" + Application.streamingAssetsPath + "/本地下载测试.png");
//等待服务器端响应后,断开连接后,再继续执行后面的内容
yield return unityWebRequest.SendWebRequest();
//如果处理成功,结果就是成功的枚举
if (unityWebRequest.result == UnityWebRequest.Result.Success)
{
//(unityWebRequest.downloadHandler as DownloadHandlerTexture).texture //获取数据
image.texture = DownloadHandlerTexture.GetContent(unityWebRequest); //更快的获取数据
}
else
{
print("获取失败:" + unityWebRequest.result + unityWebRequest.error + unityWebRequest.responseCode);
}
}

以下载图片为例,如果想要显示下载进度和下载字节数,直接执行 unityWebRequest.SendWebRequest()
然后每帧检测是否下载完毕,然后输出下载进度和下载字节数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
IEnumerator LoadTexture()
{
//UnityWebRequest unityWebRequest = UnityWebRequestTexture.GetTexture("http://192.168.43.144/Http_Server/下载测试.png");
UnityWebRequest unityWebRequest = UnityWebRequestTexture.GetTexture("ftp://192.168.43.144/下载测试.png");
//UnityWebRequest unityWebRequest = UnityWebRequestTexture.GetTexture("file://" + Application.streamingAssetsPath + "/本地下载测试.png");
//等待服务器端响应后,断开连接后,再继续执行后面的内容
unityWebRequest.SendWebRequest();
while (!unityWebRequest.isDone)
{
print($"下载了{unityWebRequest.downloadedBytes}字节");
print($"下载到{unityWebRequest.downloadProgress * 100}%");
yield return null;
}
print($"下载了{unityWebRequest.downloadedBytes}字节");
print($"下载到{unityWebRequest.downloadProgress * 100}%");
//如果处理成功,结果就是成功的枚举
if (unityWebRequest.result == UnityWebRequest.Result.Success)
{
//(unityWebRequest.downloadHandler as DownloadHandlerTexture).texture //获取数据
image.texture = DownloadHandlerTexture.GetContent(unityWebRequest); //更快的获取数据
}
else
{
print("获取失败:" + unityWebRequest.result + unityWebRequest.error + unityWebRequest.responseCode);
}
}

image

获取AB包

纹理(图片)的获取使用 UnityWebRequestAssetBundle.GetAssetBundle()​方法传入地址创建 unityWebRequest​ 对象,即可挂起协程等待获取完毕

获取完毕后,使用 (unityWebRequest.downloadHandler as DownloadHandlerAssetBundle).assetBundle​ 通过转换获取AB包
但是更方便的获取方法是,调用 DownloadHandlerAssetBundle.GetContent()​ 方法,传入 unityWebRequest​ 对象,即可获取AB包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void Start()
{
StartCoroutine(LoadAB());
}

IEnumerator LoadAB()
{
UnityWebRequest unityWebRequest = UnityWebRequestAssetBundle.GetAssetBundle("http://192.168.43.144/Http_Server/lua");
yield return unityWebRequest.SendWebRequest();
//如果处理成功,结果就是成功的枚举
if (unityWebRequest.result == UnityWebRequest.Result.Success)
{
//AssetBundle ab = (unityWebRequest.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
AssetBundle ab = DownloadHandlerAssetBundle.GetContent(unityWebRequest);
print(ab.name);
}
else
{
print("获取失败:" + unityWebRequest.result + unityWebRequest.error + unityWebRequest.responseCode);
}
}

上传相关数据类

父接口:IMultipartFormSection​,数据相关类都继承该接口,我们可以用父类装子类

我们在使用 Post​ 发送数据时,经常会发送多个数据,因此我们可以声明一个 IMultipartFormSection​ 的 List​ 来装载不同类型的数据

1
List<IMultipartFormSection> dataList = new List<IMultipartFormSection>();

我们声明这样的 List​ 后,就可以将其子类数据直接添加到列表内,然后将列表一起通过 UnityWebRequest​ 上传到服务器

​​IMultipartFormSection​​的子类数据

  • MultipartFormDataSection​,它一般用来传递一些键值对的数据,传递上去的数据需要服务器的后端程序员来处理

    该类的构造函数有多种重载:

    1. 二进制字节数组

      值得一提的是,我们一般很少直接用二进制字节数组,除非和服务器的后端商量好如何处理字节数组

      1
      2
      List<IMultipartFormSection> dataList = new List<IMultipartFormSection>();
      dataList.Add(new MultipartFormDataSection(Encoding.UTF8.GetBytes("二进制字节数组测试")));
    2. 字符串

      同样我们需要和服务器的后端商量好如何处理字符串

      1
      2
      List<IMultipartFormSection> dataList = new List<IMultipartFormSection>();
      dataList.Add(new MultipartFormDataSection("字符串测试"));
    3. 参数名,参数值(字节数组,字符串),编码类型,资源类型(常用)

      如果参数值是字符串形式的,第三个参数就可以填入字符编码,第四个参数则是资源类型,如何填参考:ContentType的常用类型
      如果参数值是字节数组形式的,第三个参数就是资源类型

      1
      2
      3
      List<IMultipartFormSection> dataList = new List<IMultipartFormSection>();
      dataList.Add(new MultipartFormDataSection("Name", "MrTang", Encoding.UTF8, "application/..."));
      dataList.Add(new MultipartFormDataSection("Msg", new byte[1024], "application/..."));
  • MultipartFormFileSection​,它用来把文件的二进制数据传递到服务器上,相当于传文件,
    与以前的 WWW​ 和 WWWFrom​ 上传数据不同,为了适应 Addressables​ 上传AB包的需要,该类对象传递的文件无需做额外处理就可以直接上传到服务器

    该类的构造函数同样有多种重载:

    1. 字节数组

      直接传输文件的二进制数据,不常用

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

      上传的时候会将文件名一并传输出去

      1
      dataList.Add(new MultipartFormFileSection("上传的文件名.png", File.ReadAllBytes(Application.streamingAssetsPath + "/test.png")));
    3. 字符串数据,文件名(常用)

      将参数一的字符串传输出去后创建为文本文件,文本文件名由参数二决定,

      1
      dataList.Add(new MultipartFormFileSection("123123123123", "test.txt"));
    4. 字符串数据,编码格式,文件名(常用)

      将参数一的字符串传输出去后创建为文本文件,文本文件编码格式由参数二决定,文本文件名由参数三决定

      1
      dataList.Add(new MultipartFormFileSection("123123123123", Encoding.UTF8, "test.txt"));
    5. 表单名,字节数组,文件名,文件类型

      表单名是HTTP通信时使用的名字,参数二、三和传输文件二进制数据时使用的参数一致,参数四是资源类型,填法:ContentType的常用类型

      1
      dataList.Add(new MultipartFormFileSection("file", new byte[1024], "test.txt", "application/..."));
    6. 表单名,字符串数据,编码格式,文件名

      表单名是HTTP通信时使用的名字,参数二、三、四和传输文本文件时使用的参数一致

      1
      dataList.Add(new MultipartFormFileSection("file", "123123123", Encoding.UTF8, "test.txt"));

上传相关API

  • Post​ 创建上传数据请求

    参数一是上传数据到哪个地址,参数二是上传的数据

    Post​ 有多种重载,比较重要的是参数二的重载,对应了上传数据的各种形式

    参数二的上传数据可以传入一个字符串,一个 WWWFrom​ 对象,只有字符串消息字典形式的键值对消息,以及 List<IMultipartFormSection>
    其中 List<IMultipartFormSection>​ 类型的参数最为常用,因为它能将多个键值对消息和文件数据一起上传到服务端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    List<IMultipartFormSection> data = new List<IMultipartFormSection>();
    //键值对相关的消息数据
    data.Add(new MultipartFormDataSection("Name", "MrTang"));
    //添加二进制数据文件
    data.Add(new MultipartFormFileSection("Test123.png", File.ReadAllBytes(Application.streamingAssetsPath + "/test.png")));
    //添加文本文件
    data.Add(new MultipartFormFileSection("123123123", "Test123.txt"));
    //使用Post方法创建上传请求
    UnityWebRequest unityWebRequest = UnityWebRequest.Post("http://192.168.1.102/Http_Server/", data);
  • Put​ 创建上传数据请求

    注意:Put​ 请求类型不是所有的web服务器都认,它是HTTP/1.1的新请求,必须要服务器能够处理该请求类型那么才能有响应

    Put​ 可以上传字符串形式和字节数据形式的数据,需要后端程序员配合处理

    1
    2
    3
    4
    //读取某个文件的二进制数据
    byte[] bytes = File.ReadAllBytes(Application.streamingAssetsPath + "/test.png");
    //设置上传地址和上传内容
    UnityWebRequest unityWebRequest = UnityWebRequest.Put("http://192.168.1.102/Http_Server/", bytes);
  • 当前上传的字节数

    1
    print($"上传字节数:{unityWebRequest.uploadedBytes}");
  • 当前上传的进度(0~1)

    1
    print($"上传进度:{unityWebRequest.uploadProgress * 100}%");

Post发送操作

开启上传需要的协程后,我们先创建一个 List<IMultipartFormSection>​ 的列表,便于我们一次将多个文件上传上去
然后就可以向列表添加键值对类型的数据 MultipartFormDataSection​,添加文件数据 MultipartFormDataSection
添加完毕后,通过 UnityWebRequest.Post​ 传入要上传的地址和列表,创建一个 UnityWebRequest​ 请求

我们可以直接 yield return unityWebRequest.SendWebRequest()​ 以挂起协程等待上传结束,
也可以只执行 unityWebRequest.SendWebRequest()​,然后每帧输出上传字节数和进度并挂起协程

上传结束后,我们通过 unityWebRequest.result​ 来检测是否上传成功

值得一提的是,如果服务端的后端程序员配合,在上传完数据后会返回一个数据,
我们就可以通过 unityWebRequest.downloadHandler.data​ 来获取它,相当于一起完成了上传与下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
void Start()
{
StartCoroutine(Upload());
}

IEnumerator Upload()
{
//准备上传的数据
List<IMultipartFormSection> data = new List<IMultipartFormSection>();
//键值对相关的消息数据
data.Add(new MultipartFormDataSection("Name", "MrTang"));
PlayerMessage message = new PlayerMessage();
//初始化数据
message.playerID = 111;
message.playerData = new PlayerData();
message.playerData.name = "唐老狮";
message.playerData.atk = 99;
message.playerData.lev = 1000;
data.Add(new MultipartFormDataSection("Msg", message.Writeing())); //Writeing是将数据序列化为直接数组
//添加一些文件上传文件
//二进制数据文件
data.Add(new MultipartFormFileSection("Test123.png", File.ReadAllBytes(Application.streamingAssetsPath + "/test.png")));
//文本文件
data.Add(new MultipartFormFileSection("123123123", "Test123.txt"));
//使用Post方法创建上传请求
UnityWebRequest unityWebRequest = UnityWebRequest.Post("http://192.168.1.102/Http_Server/", data);
//正式执行请求
unityWebRequest.SendWebRequest();
//循环输出上传进度和上传字节数
while (!unityWebRequest.isDone)
{
print($"上传进度:{unityWebRequest.uploadProgress * 100}%");
print($"上传字节数:{unityWebRequest.uploadedBytes}");
yield return null;
}
print($"上传进度:{unityWebRequest.uploadProgress * 100}%");
print($"上传字节数:{unityWebRequest.uploadedBytes}");
if (unityWebRequest.result == UnityWebRequest.Result.Success)
{
print("上传成功");
//如果返回了消息,可以使用unityWebRequest.downloadHandler.data来处理
}
else
print("上传失败:" + unityWebRequest.result + unityWebRequest.error + unityWebRequest.responseCode);
}

值得一提的是,这些 IMultipartFormSection​ 派生出来的数据类,以及 UnityWebRequest​ 上传的底层逻辑,
其实就有点类似于我们在 使用Http上传数据 时按照Http上传数据的规则所做的拼接工作,
上传多个数据类其实就是Unity底层帮助我们将这些类对象中的数据按照Http上传数据的规则拼接起来,再上传到服务器上

Put上传相关

注意:Put​ 请求类型不是所有的web服务器都认,它是HTTP/1.1的新请求,必须要服务器能够处理该请求类型那么才能有响应

Put​ 主要就是上传文件的请求,所以当服务器发现这是一个 Put​ 类型的请求就会进行相对应的处理

它的实现逻辑相对 Post​ 来说更加简单,但是同样需要后端程序员配合完成

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void Start()
{
StartCoroutine(UploadPut());
}

IEnumerator UploadPut()
{
//读取某个文件的二进制数据
byte[] bytes = File.ReadAllBytes(Application.streamingAssetsPath + "/test.png");
//设置上传地址和上传内容
UnityWebRequest unityWebRequest = UnityWebRequest.Put("http://192.168.1.102/Http_Server/", bytes);
//挂起协程直到上传完毕
yield return unityWebRequest.SendWebRequest();
if (unityWebRequest.result == UnityWebRequest.Result.Success)
{
print("Put上传成功");
}
else
{
print("Put上传失败!");
}
}