使用 HTTP 可以请求下载文件,response 的结果就是文件的内容。
在下载一个文件之前可以先使用 HEAD
请求来只获取头,可以从 Content-Length
头获取到文件的大小。
1 | // head request |
在 UE 中需要通过监听 HTTP 请求的 OnHeaderReceived
派发来获得想要的头数据:
1 | void UDownloadProxy::OnRequestHeadHeaderReceived(FHttpRequestPtr RequestPtr, const FString& InHeaderName, const FString& InNewHeaderValue) |
之后就可以用 Get
方法来请求文件了:
1 | // get request |
其中 Range 头的格式为:Byte=0-
指请求整个文件的大小,Byte=0-99
则是请求前 100byte,注意请求的范围不要超过文件大小 ,不然会有 400 错误。
通过控制 HTTP 请求的 Range
头,我们可以指定下载文件的任意部分,可以实现暂停继续 / 分片下载。
在 UE 中使用 HTTP 请求一个大文件的时候,如果该请求没有结束就去拿 response 的结果一定要注意一个问题:那就是 Response 的 Content 数据
Payload
是一个TArray
动态数组,当 Content 的内容不断地被写入,会导致容器的Reserve
也就是内存重新分配,获取该数组的内存地址是非常危险的。
所以建议在 HTTP 请求时先对 response 的 Content 的 Payload
进行 Reserve
使其能够容纳足够数量的数据,缺点就是会一次性占用整个文件的内存。
解决内存占用的办法就是通过 Http
请求的 Range
来实现分片下载(也就是把一个大文件分成数个小块,一块一块地下载),从而降低内存占用,
当下载文件后,通常还有进行文件校验的操作,等文件下载完之后再执行校验(如 MD5 计算)时间会很长,所以要解决校验的时间问题,想过开一个线程去计算,但是开线程只解决了不阻塞主线程,不会加速 MD5 的计算过程,后来想到 MD5
是摘要计算,进而联想到可不可以边下边进行 MD5 计算,根据 没有全新的轮子定理 (我瞎掰的),我查到了 OpenSSL 中的 MD5 实现支持使用MD5_Update
来增量计算,所以这个问题就迎刃而解了,具体看我前面的笔记MD5 的分片校验。
基于上面这些内容,可以实现一个简陋的下载器功能了,可作为游戏中的下载组件,虽然看似简单,但是设计一个合理的结构和没有 bug 的版本还是要花点功夫的。
我把上面介绍的内容写成了一个插件:
HTTP 的分片请求在服务端的 Log:
资料文档: