UN4L9——UnityWebRequest高级操作
UN4L9——UnityWebRequest高级操作
本章代码关键字
1 | //下载请求属性相关 |
UnityWebRequest高级操作
在常用操作中我们使用的是Unity为我们封装好的一些方法,我们可以方便的进行一些指定类型的数据获取,之前学习的常规操作:
1 | //已经学习过的内容 |
比如,下载数据时:
- 文本和2进制
- 图片
- AB包
如果我们想要获取其它类型的数据应该如何处理呢?
上传数据时:
- 可以指定参数和值
- 可以上传文件
如果想要上传一些基于HTTP规则的其它数据应该如何处理呢?
高级操作就是用来处理常用操作不能完成的需求的, 它的核心思想就是:UnityWebRequest中可以将数据处理分离开
比如常规操作中我们用到的 DownloadHandlerTexture
和 DownloadHandlerAssetBundle
两个类,就是用来将二进制字节数组转换成对应类型进行处理的
所以高级操作是指:让你按照规则来实现更多的数据获取、上传等功能
我们可以自己设置UnityWebRequest当中的下载处理对象,当设置后,下载数据后它会使用该对象中对应的函数处理数据
让我们更方便的获取我们想要的数据,方便我们对数据下载或获取进行拓展
UnityWebRequest类的更多内容
注:这里与上篇笔记存在完全重复的部分,因此已经在上篇记录过的内容被笔者删去,相关内容请去上篇查阅
-
构造函数
除了
Get
、Post
等一系列静态方法可以创建UnityWebRequest
以外,构造函数同样可以创建构造函数有多种重载,对应不同的情况,构造函数不传入任何值也可以,因为构造函数内设置的值也可以在实例化后对相应的属性进行设置
- 参数一:上传/下载地址,可以使用字符串也可以使用
Uri
- 参数二:请求类型,类似于设置操作命令,传入字符串,可以使用
UnityWebRequest
内部声明好的常量来调用 - 参数三:下载处理类,设置何种处理类来处理下载数据
- 参数四:上传处理类,设置何种处理类来处理上传数据
1
UnityWebRequest request = new UnityWebRequest();
- 参数一:上传/下载地址,可以使用字符串也可以使用
-
请求地址
该属性可以在使用构造函数实例化对象时就设置好,上传或者下载使用的地址
1
request.url = "服务器地址";
-
请求类型
该属性可以在使用构造函数实例化对象时就设置好,
UnityWebRequest
内部声明了一系列的常量便于我们调用请求类型字符串1
request.method = UnityWebRequest.kHttpVerbPOST;
-
超时设置
设置上传和下载的请求时间,超时就报错
1
request.timeout = 2000;
-
重定向次数 设置为0表示不进行重定向 可以设置次数
重定向指的是,设置的连接让你跳转到别的连接上,设置次数可以限制重定向的次数,设置为0就是不允许重定向
1
unityWebRequest.redirectLimit = 10;
-
下载、上传处理对象
该属性可以在使用构造函数实例化对象时就设置好
本课的核心内容,
unityWebRequest
从服务端下载到的数据如何处理,就取决于unityWebRequest.downloadHandler
设置的类中的实现
该属性需要的类是DownloadHandler
派生类,派生类如何实现决定了如何处理下载到的数据而对于上传,我们同样也可以使用
UploadHandler
派生类来直接上传字节数据或文件,而不使用Unity实现的处理逻辑这就是下载/上传数据和数据处理分离的体现
1
2unityWebRequest.downloadHandler = new DownloadHandlerBuffer();
unityWebRequest.uploadHandler = new UploadHandlerFile(Application.streamingAssetsPath + "/test.png");
自定义获取数据的类——DownloadHandler派生类
以下的这些类,其实就是Unity帮助我们实现好的,用于解析下载下来的数据的类
使用对应的类处理下载数据,他们就会在内部将下载的数据处理为对应的类型,方便我们使用
关键类:
-
DownloadHandlerBuffer
:用于简单的数据存储,得到对应的2进制数据假设我们要从服务端中的一个文本文档内读取字符串并输出出来,且不调用
UnityWebRequest.Get()
来创建请求我们直接使用构造方法实例化一个
UnityWebRequest
对象,传入获取地址和请求类型,
也可以不在构造函数里直接设置以上的内容,可以在实例化之后直接对相应的属性赋值,如url
、method
然后将
UnityWebRequest
对象的下载处理类属性downloadHandler
赋值为一个DownloadHandlerBuffer
对象之后在下载完数据后,我们就可以通过
DownloadHandlerBuffer
对象直接获取下载到的二进制数据,对其进行处理就可以输出字符串相比常规操作获取二进制数据,这里我们是直接从我们自己声明的
DownloadHandlerBuffer
对象中获取数据,没有通过UnityWebRequest
对象获取实际上,常规操作中获取二进制数据本质也是通过
unityWebRequest.downloadHandler
属性中的DownloadHandlerBuffer
对象获取的
只不过,常规操作中的DownloadHandlerBuffer
对象是在调用UnityWebRequest.Get()
时实例化的
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
29void Start()
{
StartCoroutine(DownLoadString());
}
IEnumerator DownLoadString()
{
//地址、请求类型、上传下载方法可以在实例化后设置,也可以在构造函数内直接一步到位
//UnityWebRequest request = new UnityWebRequest()
//request.url = "http://192.168.1.102/Http_Server/test.txt"
//request.method = UnityWebRequest.kHttpVerbGET
//从服务器的一个文本文件里读取字符串并输出出来
UnityWebRequest request = new UnityWebRequest("http://192.168.1.102/Http_Server/test.txt",
UnityWebRequest.kHttpVerbGET);
//实例化一个DownloadHandlerBuffer对象,在这里使用局部变量的原因是后面还需要调用
DownloadHandlerBuffer bufferHandler = new DownloadHandlerBuffer();
request.downloadHandler = bufferHandler;
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
//数据下载完毕后,就可以从DownloadHandlerBuffer对象取出下载到的数据
byte[] bytes = bufferHandler.data;
print(Encoding.UTF8.GetString(bytes));
}
else
{
print("获取数据失败" + request.result + request.error + request.responseCode);
}
} -
DownloadHandlerFile
:用于下载文件并将文件保存到磁盘(内存占用少)假设我们要从服务端下载图片(或者任意文件)到本地磁盘上,靠原来的常规操作不能一步到位
先直接使用构造方法实例化一个
UnityWebRequest
对象,传入获取文件地址和请求类型然后实例化一个
DownloadHandlerFile
对象,赋值到UnityWebRequest
的downloadHandler
属性上,构造函数的参数为:- 参数一:下载文件到哪个路径下
- 参数二:是否在已下载文件的后面追加(可选),主要用于在原来已经下载好的文件上添加内容,如果要覆盖则填
false
或者不填
下载完毕后,下载好的文件会自动保存到指定的路径下,无需做额外操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22void Start()
{
StartCoroutine(DownLoadTex());
}
IEnumerator DownLoadTex()
{
UnityWebRequest unityWebRequest = new UnityWebRequest("http://192.168.1.102/Http_Server/下载测试.png",
UnityWebRequest.kHttpVerbGET);
unityWebRequest.downloadHandler = new DownloadHandlerFile(Application.persistentDataPath + "/downloadFile.jpg");
yield return unityWebRequest.SendWebRequest();
if (unityWebRequest.result == UnityWebRequest.Result.Success)
{
//byte[] bytes = bufferHandler.data;
print(Application.persistentDataPath + "/downloadFile.jpg");
}
else
{
print("获取数据失败" + unityWebRequest.result + unityWebRequest.error + unityWebRequest.responseCode);
}
} -
DownloadHandlerTexture
:用于下载图像假设我们要从服务端下载贴图,将贴图使用在UI上,且不使用
UnityWebRequestTexture.GetTexture()
方法先直接使用构造方法实例化一个
UnityWebRequest
对象,传入获取文件地址和请求类型
然后实例化一个DownloadHandlerTexture
对象,赋值到UnityWebRequest
的downloadHandler
属性上等待下载完毕后,就可以直接调用
DownloadHandlerTexture
对象通过texture
属性上获取贴图相比常规操作,同样我们是直接从我们自己声明的
DownloadHandlerTexture
对象中获取贴图,没有通过UnityWebRequest
对象获取实际上,常规操作中获取贴图本质也是通过
unityWebRequest.downloadHandler
属性中的DownloadHandlerTexture
对象获取的
只不过,常规操作中的
DownloadHandlerTexture
对象是在调用UnityWebRequestTexture.GetTexture()
时实例化的
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
26public RawImage image;
void Start()
{
StartCoroutine(DownLoadAB());
}
IEnumerator DownLoadTex()
{
UnityWebRequest request = new UnityWebRequest("http://192.168.1.102/Http_Server/下载测试.png",
UnityWebRequest.kHttpVerbGET);
//实例化一个DownloadHandlerTexture对象,在这里使用局部变量的原因是后面还需要调用
DownloadHandlerTexture textureHandler = new DownloadHandlerTexture();
request.downloadHandler = textureHandler;
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
//数据下载完毕后,就可以从DownloadHandlerTexture对象的texture属性取出下载到的贴图
image.texture = textureHandler.texture;
}
else
{
print("获取数据失败" + request.result + request.error + request.responseCode);
}
} -
DownloadHandlerAssetBundle
:用于提取 AssetBundle。假设我们要从服务端AB包,且不使用
UnityWebRequestAssetBundle.GetAssetBundle()
方法先直接使用构造方法实例化一个
UnityWebRequest
对象,传入获取文件地址和请求类型
然后实例化一个DownloadHandlerAssetBundle
对象,赋值到UnityWebRequest
的downloadHandler
属性上等待下载完毕后,就可以直接调用
DownloadHandlerAssetBundle
对象通过assetBundle
属性上获取AB包相比常规操作,同样我们是直接从我们自己声明的
DownloadHandlerAssetBundle
对象中获取贴图,没有通过UnityWebRequest
对象获取同样,常规操作中获取贴图本质也是通过
unityWebRequest.downloadHandler
属性中的DownloadHandlerAssetBundle
对象获取的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24void Start()
{
StartCoroutine(DownLoadAB());
}
IEnumerator DownLoadAB()
{
UnityWebRequest request = new UnityWebRequest("http://192.168.1.102/Http_Server/lua",
UnityWebRequest.kHttpVerbGET);
//第二个参数是校验码,用于比较文件完整性,但是校验码必须已知才能检验,如果不校验,传入0,即不进行检查
//一般只有在进行AB包热更新时,服务器发送了对应的文件列表中,包含了校验码,才能进行检查
DownloadHandlerAssetBundle assetBundleHandler = new DownloadHandlerAssetBundle(request.url, 0);
request.downloadHandler = assetBundleHandler;
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
AssetBundle ab = assetBundleHandler.assetBundle;
print(ab.name);
}
else
{
print("获取数据失败" + request.result + request.error + request.responseCode);
}
} -
DownloadHandlerAudioClip
:用于下载音频文件与上述四个类需要我们自行声明不同,这里的获取请求方法更类似于常规方法里获取纹理、AB包那样
使用
UnityWebRequestMultimedia.GetAudioClip()
创建UnityWebRequest
对象,该方法的参数是:- 参数一:文件地址
- 参数二:媒体类型,由下载的音频决定
下载完毕后,通过
DownloadHandlerAudioClip.GetContent()
传入UnityWebRequest
对象来获取音频文件1
2
3
4
5
6
7
8
9
10
11
12
13
14IEnumerator DownLoadAudioClip()
{
UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip("http://192.168.1.102/Http_Server/音效文件.mp3",
AudioType.MPEG);
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
AudioClip clip = DownloadHandlerAudioClip.GetContent(request);
}
else
{
print("获取数据失败" + request.result + request.error + request.responseCode);
}
} -
DownloadHandlerScript
:可自定义数据处理的基类
DownloadHandlerScript
是一个特殊类。就其本身而言,不会执行任何操作。
但是,此类可由用户定义的类继承。此类接收来自UnityWebRequest
系统的回调,
然后可以使用这些回调在数据从网络到达时执行完全自定义的数据处理。在继承
DownloadHandlerScript
后,我们需要自己重写对应的成员,以下是常用的会重写的成员:- 构造函数:我们可以根据需求,去声明多种构造函数,
但是在调用构造函数的时候一定要调用基类的构造函数!!
如:public CustomDownLoadFileHandler() : base() { }
,我们必须调用这个base()
如果构造函数的参数有byte[]
,则需要调用对应参数为byte[]
的基类构造函数
如:public CustomDownLoadFileHandler(byte[] bytes) : base(bytes) { }
-
GetData()
:返回下载数据对应的字节数组 -
ReceiveContentLengthHeader()
:从服务器收到Content-Length表头时会调用的方法,即正式下载前会调用的方法****参数为接下来要下载的数据大小,可用于初始化接收数据的容器 -
ReceiveData()
:从网络收到数据后,每帧会自动调用的方法,参数为本帧收到的数据的字节数组,以及该字节数组长度 -
CompleteContent()
:当消息全部接收完了,会自动调用的方法
假设我们要自己实现一个能够将下载下来的数据处理为文件并保存到本地硬盘上,我们主要这样重写:
-
CustomDownLoadFileHandler(string path) : base()
:外部实例化时可指定下载文件到哪里的路径,为savePath
赋值 -
GetData()
:不使用基类的返回结果,而是返回派生类里声明的缓存字节数组cacheBytes
-
ReceiveData()
:根据传入的本帧下载数据和长度,将下载的内容拷贝到缓存字节数组cacheBytes
中,同时输出本帧下载数据的长度 -
ReceiveContentLengthHeader()
:根据传入的接下来要下载的数据大小,为cacheBytes
赋值相等长度的字节数组,确保其可以完整接收数据 -
CompleteContent()
:下载完所有数据后,将下载到的数据写到本地硬盘上
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85//using ...
public class Lesson32 : MonoBehaviour
{
void Start()
{
StartCoroutine(DownLoadCustomHandler());
}
IEnumerator DownLoadCustomHandler()
{
UnityWebRequest request = new UnityWebRequest("http://192.168.1.102/Http_Server/下载测试2.png",
UnityWebRequest.kHttpVerbGET);
request.downloadHandler = new CustomDownLoadFileHandler(Application.persistentDataPath + "/CustomHandler.jpg");
yield return request.SendWebRequest();
if (request.result == UnityWebRequest.Result.Success)
{
print("存储到本地成功!" + Application.persistentDataPath + "/CustomHandler.jpg");
}
else
{
print("获取数据失败" + request.result + request.error + request.responseCode);
}
}
}
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 base.GetData();
return cacheBytes;
}
/// <summary>
/// 从网络收到数据后,每帧会自动调用的方法
/// </summary>
/// <param name="data"></param>
/// <param name="dataLength"></param>
/// <returns></returns>
protected override bool ReceiveData(byte[] data, int dataLength)
{
//return base.ReceiveData(data, dataLength);
Debug.Log("收到数据长度(取字节数组长度):" + data.Length);
Debug.Log("收到数据长度(取dataLength):" + dataLength);
data.CopyTo(cacheBytes, index);
index += dataLength;
return true;
}
/// <summary>
/// 从服务器收到Content-Length表头时会调用的方法
/// </summary>
/// <param name="contentLength"></param>
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);
}
} - 构造函数:我们可以根据需求,去声明多种构造函数,
可以发现,在使用上述的DownloadHandler
派生类处理下载的不同的数据时,
UnityWebRequest
对象的实例化,赋值,执行的方法等逻辑基本是一致的
不同的点主要是在UnityWebRequest
对象的downloadHandler
属性中,不同的DownloadHandler
派生类所导致的
因此,UnityWebRequest
的下载数据和处理下载数据逻辑是分开的,
发送请求,下载数据的逻辑是在UnityWebRequest
内部实现的,
而下载到的数据的具体如何处理是由不同的DownloadHandler
派生类决定的
因此,通过为UnityWebRequest
对象downloadHandler
属性赋值特定的DownloadHandler
派生类对象,即可选择特定的处理逻辑对下载的数据进行处理
对于自定义的数据,以及它如何处理,我们可以继承DownloadHandlerScript
,在派生类自己实现处理逻辑
自定义上传数据的类——UploadHandler派生类
注意:由于UnityWebRequest
类的常用操作中,上传数据相关内容已经封装的很好了
我们可以很方便的上传参数和文件,我们使用常用操作已经能够满足常用需求了,所以对于UploadHandler
相关类的内容主要做了解
下面的各个方法,需要由后端程序员配合,对上传的数据进行处理,它们才能生效,否则是不起作用的
UploadHandler
派生类中比较重要的变量是contentType
内容类型,
如果不设置,模式是application/octet-stream
,2进制流的形式,
关于该变量可以传入什么内容,请看:ContentType的常用类型
-
UploadHandlerRaw
用于上传字节数组值得一提的是,也可以像C#中按照Http的规则来生成字节数组,可以达到上传文件的目的(但是并没有这个必要)
将要上传的消息序列化为字节数组,然后在使用构造函数实例化UploadHandlerRaw
对象时,传入这个字节数组即可1
2
3
4
5
6
7
8
9
10
11
12
13
14
15void Start()
{
StartCoroutine(UpLoad());
}
IEnumerator UpLoad()
{
UnityWebRequest request = new UnityWebRequest("http://192.168.1.102/Http_Server/",
UnityWebRequest.kHttpVerbPOST);
byte[] bytes = Encoding.UTF8.GetBytes("123123123123123");
request.uploadHandler = new UploadHandlerRaw(bytes);
//request.uploadHandler.contentType = "类型/细分类型";
yield return request.SendWebRequest();
print(request.result);
} -
UploadHandlerFile
用于上传文件在使用构造函数实例化
UploadHandlerRaw
对象时,传入要上传的文件路径即可,对于不同的文件类型可以设置contentType
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15void Start()
{
StartCoroutine(UpLoad());
}
IEnumerator UpLoad()
{
UnityWebRequest request = new UnityWebRequest("http://192.168.1.102/Http_Server/",
UnityWebRequest.kHttpVerbPOST);
request.uploadHandler = new UploadHandlerFile(Application.streamingAssetsPath + "/test.png");
//request.uploadHandler.contentType = "类型/细分类型";
yield return request.SendWebRequest();
print(request.result);
}