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上传失败!");
}
}