123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- //
- // RUDownloader.cpp
- // red_utils
- //
- // Created by RedInfinity on 2023/12/6.
- //
- #include "RUDownloader.hpp"
- ///C++标准库
- #include <iomanip>
- #include <sstream>
- #include <iostream>
- NS_RU_BEGIN
- ///下载器日志
- #if CC_TARGET_PLATFORM == CC_PLATFORM_MAC
- #define REDUTILS_DOWNLOADER_LOG(format, ...) cocos2d::log("【RUDownloader】" format, ##__VA_ARGS__)
- #else
- #define REDUTILS_DOWNLOADER_LOG(format, ...) do {} while (0)
- #endif
- ///调用下载器委托的指定方法
- #define INVOKE_REDUTILS_DOWNLOADER_DELEGATE_METHOD(delegate, method, ...) do { if (delegate) { (delegate)->method(__VA_ARGS__); } } while (0)
- Downloader* Downloader::create(const DownloaderCreateData& data, DownloaderDelegate* delegate) {
- Downloader* ret = new Downloader();
- ret->_init(data, delegate);
- return ret;
- }
- void Downloader::release() {
- delete _innerDownloader;
- delete this;
- }
- bool Downloader::downloadFile(const std::string& url, const std::string& saveDir) {
- DownloadRequestData requestData;
- requestData.url = url;
- requestData.saveDir = saveDir;
- return _appendDownloadRequest(requestData);
- }
- bool Downloader::downloadFileAndVerifyMd5(const std::string& url, const std::string& saveDir, const std::string& md5) {
- DownloadRequestData requestData;
- requestData.url = url;
- requestData.saveDir = saveDir;
- requestData.verify = true;
- requestData.md5 = md5;
- return _appendDownloadRequest(requestData);
- }
- void Downloader::_init(const DownloaderCreateData& data, DownloaderDelegate* delegate) {
- _id = data.id;
- _innerDownloader = _createInnerDownloader(data.maxDownloadCnt);
- _delegate = delegate;
- ///初始化内置的cocos2d下载器
- _initInnerDownloader();
- }
- cocos2d::network::Downloader* Downloader::_createInnerDownloader(const kMaxDownloadCnt& maxDownloadCnt) {
- cocos2d::network::DownloaderHints hints;
- hints.countOfMaxProcessingTasks = static_cast<int>(maxDownloadCnt);
- hints.timeoutInSeconds = 45;
- hints.tempFileNameSuffix = ".tmp";
- return new cocos2d::network::Downloader(hints);
- }
- void Downloader::_initInnerDownloader() {
- ///文件下载成功
- _innerDownloader->setOnFileTaskSuccess([this](const cocos2d::network::DownloadTask& task) {
- ///移除下载请求
- const std::string& decodeURL = _decodeURL(task.requestURL);
- DownloadRequestData requestData = _removeDownloadRequest(decodeURL);
- if (requestData.verify) {
- ///如果是需要验证的先验证下载完成的文件
- const std::string& saveFilePath = requestData.saveDir + _getFileNameByUrl(requestData.url);
- if (_verifyFileMd5(saveFilePath, requestData.md5)) {
- ///通知文件下载成功
- INVOKE_REDUTILS_DOWNLOADER_DELEGATE_METHOD(_delegate, onDownloadFileSuccess, requestData.url);
- REDUTILS_DOWNLOADER_LOG("下载成功并验证通过:%s", requestData.url.c_str());
- } else {
- ///通知文件下载失败
- INVOKE_REDUTILS_DOWNLOADER_DELEGATE_METHOD(_delegate, onDownloadFileFail, requestData.url, "file md5 verify fail");
- REDUTILS_DOWNLOADER_LOG("下载成功但验证失败:%s", requestData.url.c_str());
- }
- } else {
- ///通知文件下载成功
- INVOKE_REDUTILS_DOWNLOADER_DELEGATE_METHOD(_delegate, onDownloadFileSuccess, requestData.url);
- REDUTILS_DOWNLOADER_LOG("下载成功:%s", requestData.url.c_str());
- }
- ///完成下载
- _addDownloadedCnt();
- ///计算下载速度
- _updateDownloadSpeed(requestData.url);
- });
-
- ///文件下载失败
- _innerDownloader->setOnTaskError([this](const cocos2d::network::DownloadTask& task,
- int errorCode,
- int errorCodeInternal,
- const std::string& errorStr) {
- ///移除下载请求
- const std::string& decodeURL = _decodeURL(task.requestURL);
- DownloadRequestData requestData = _removeDownloadRequest(decodeURL);
- ///通知文件下载失败
- INVOKE_REDUTILS_DOWNLOADER_DELEGATE_METHOD(_delegate, onDownloadFileFail, requestData.url, errorStr);
- ///完成下载
- _addDownloadedCnt();
- ///计算下载速度
- _updateDownloadSpeed(requestData.url);
- REDUTILS_DOWNLOADER_LOG("下载失败:%s", requestData.url.c_str());
- });
-
- ///文件下载进行中
- _innerDownloader->setOnTaskProgress([this](const cocos2d::network::DownloadTask& task,
- int64_t bytesReceived,
- int64_t totalBytesReceived,
- int64_t totalBytesExpected) {
- const float& percent = static_cast<float>(totalBytesReceived * 100) / static_cast<float>(totalBytesExpected);
- ///通知文件下载百分比
- const std::string& decodeURL = _decodeURL(task.requestURL);
- INVOKE_REDUTILS_DOWNLOADER_DELEGATE_METHOD(_delegate, onDownloadFileProgress, decodeURL, static_cast<int>(percent));
- ///因为cocos2d的下载没有开始下载的生命周期函数,所以需要在第一次有进度时记录下
- _recordDownloadStartTime(decodeURL);
- _updateDownloadedByte(decodeURL, totalBytesReceived);
- });
- }
- std::string Downloader::_encodeURL(const std::string& url) {
- std::ostringstream ret;
- ret.fill('0');
- ret << std::hex;
- for (char c : url) {
- if (0 <= c && c <= 127) {
- ret << c;
- } else {
- ret << std::uppercase;
- ret << '%' << std::setw(2) << static_cast<int>(static_cast<unsigned char>(c));
- ret << std::nouppercase;
- }
- }
- return ret.str();
- }
- std::string Downloader::_decodeURL(const std::string& url) {
- std::ostringstream ret;
- ret.fill('0');
- ret << std::hex;
- char c = '\0';
- std::istringstream input(url);
- input.fill('0');
- while (input.get(c)) {
- if (c == '%') {
- int hexValue = 0;
- char c1 = '\0';
- char c2 = '\0';
- input.get(c1);
- input.get(c2);
- std::istringstream hexStream(std::string("0x") + c1 + c2);
- hexStream >> std::hex >> hexValue;
- ret << static_cast<char>(hexValue);
- } else {
- ret << c;
- }
- }
- return ret.str();
- }
- std::string Downloader::_getFileNameByUrl(const std::string& url) {
- return url.substr(url.rfind("/") + 1, url.size());
- }
- bool Downloader::_appendDownloadRequest(const DownloadRequestData& requestData) {
- ///请求的url地址为空
- if (requestData.url.empty()) {
- return false;
- }
- ///保存目录不存在
- if (cocos2d::FileUtils::getInstance()->isDirectoryExist(requestData.saveDir) == false) {
- return false;
- }
- ///下载请求已经存在
- if (_requestDatas.find(requestData.url) != _requestDatas.end()) {
- return false;
- }
- _requestDatas.emplace(requestData.url, requestData);
- _addTotalDownloadCnt();
- ///创建下载文件任务
- const std::string& saveFilePath = requestData.saveDir + _getFileNameByUrl(requestData.url);
- ///清理下载缓存
- _clearDownloadCache(saveFilePath);
- _innerDownloader->createDownloadFileTask(_encodeURL(requestData.url), saveFilePath, requestData.md5);
- return true;
- }
- DownloadRequestData Downloader::_removeDownloadRequest(const std::string& url) {
- DownloadRequestData ret = _requestDatas.at(url);
- _requestDatas.erase(url);
- return ret;
- }
- void Downloader::_addTotalDownloadCnt() {
- _totalDownloadCnt += 1;
- ///通知下载器下载百分比
- INVOKE_REDUTILS_DOWNLOADER_DELEGATE_METHOD(_delegate, onDownloaderPercent, _getDownloadPercent());
- ///通知下载器下载进度
- INVOKE_REDUTILS_DOWNLOADER_DELEGATE_METHOD(_delegate, onDownloaderProgress, _downloadedCnt, _totalDownloadCnt);
- }
- void Downloader::_addDownloadedCnt() {
- _downloadedCnt += 1;
- ///通知下载器下载百分比
- INVOKE_REDUTILS_DOWNLOADER_DELEGATE_METHOD(_delegate, onDownloaderPercent, _getDownloadPercent());
- ///通知下载器下载进度
- INVOKE_REDUTILS_DOWNLOADER_DELEGATE_METHOD(_delegate, onDownloaderProgress, _downloadedCnt, _totalDownloadCnt);
- }
- float Downloader::_getDownloadPercent() {
- return static_cast<float>(_downloadedCnt * 100) / static_cast<float>(_totalDownloadCnt);
- }
- bool Downloader::_verifyFileMd5(const std::string& saveFilePath, const std::string& md5) {
- const std::string& fileMd5 = cocos2d::utils::getFileMD5Hash(saveFilePath);
- if (fileMd5 == md5) {
- return true;
- }
- return false;
- }
- void Downloader::_recordDownloadStartTime(const std::string& url) {
- if (_downloadTimeClocks.find(url) == _downloadTimeClocks.end()) {
- DownloadTimeClock st = std::chrono::high_resolution_clock::now();
- _downloadTimeClocks.emplace(url, st);
- }
- }
- void Downloader::_updateDownloadedByte(const std::string& url, const int64_t& downloadedByte) {
- if (_downloadedBytes.find(url) == _downloadedBytes.end()) {
- _downloadedBytes.emplace(url, 0);
- }
- _downloadedBytes[url] = downloadedByte;
- }
- void Downloader::_updateDownloadSpeed(const std::string& url) {
- ///计算当前url的下载耗时
- double downloadTime = 0.0f;
- auto iter1 = _downloadTimeClocks.find(url);
- if (iter1 != _downloadTimeClocks.end()) {
- DownloadTimeClock et = std::chrono::high_resolution_clock::now();
- DownloadTimeClock st = iter1->second;
- _downloadTimeClocks.erase(url);
- downloadTime = std::chrono::duration_cast<std::chrono::milliseconds>(et - st).count() / 1000.0f;
- }
- ///计算当前url的下载速度
- int64_t downloadedByte = 0;
- auto iter2 = _downloadedBytes.find(url);
- if (iter2 != _downloadedBytes.end()) {
- downloadedByte = iter2->second;
- _downloadedBytes.erase(url);
- }
- int downloadSpeed = 0.0f;
- if (downloadTime != 0) {
- downloadSpeed = downloadedByte / downloadTime;
- }
- ///取上次下载速度平均下,不然会波动很大
- _downloadSpeed = (_downloadSpeed + downloadSpeed) / 2;
- ///通知下载器下载速度
- INVOKE_REDUTILS_DOWNLOADER_DELEGATE_METHOD(_delegate, onDownloaderSpeed, _downloadSpeed);
- }
- void Downloader::_clearDownloadCache(const std::string& zipFilePath) {
- ///zip文件
- if (cocos2d::FileUtils::getInstance()->isFileExist(zipFilePath)) {
- cocos2d::FileUtils::getInstance()->removeFile(zipFilePath);
- }
- ///zip临时文件
- const std::string& zipTmpFilePath = zipFilePath + ".tmp";
- if (cocos2d::FileUtils::getInstance()->isFileExist(zipTmpFilePath)) {
- cocos2d::FileUtils::getInstance()->removeFile(zipTmpFilePath);
- }
- ///解压后资源文件夹
- const std::string& uncompressFolderPath = zipFilePath.substr(0, zipFilePath.rfind("."));
- if (cocos2d::FileUtils::getInstance()->isDirectoryExist(uncompressFolderPath)) {
- cocos2d::FileUtils::getInstance()->removeDirectory(uncompressFolderPath);
- }
- }
- NS_RU_END
|