Files
LayaNative2.0/Conch/source/common/resource/JCFileResManager.cpp
T
2020-11-11 16:17:13 +08:00

797 lines
33 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
@file JCFileResManager.cpp
@brief
@author James
@version 1.0
@date 2016_5_11
*/
#include "JCFileResManager.h"
#include "../downloadCache/JCServerFileCache.h"
#include "../downloadCache/JCFileTable.h"
#include "../misc/JCWorkerThread.h"
#include "../util/JCCommonMethod.h"
#include "../util/Log.h"
#include "../util/JCLayaUrl.h"
#include "../fileSystem/JCFileSystem.h"
#include "../downloadMgr/JCCurlWrap.h"
//#include "../misc/conchDebugThread.h"
#include <algorithm>
#include "../downloadMgr/JCDownloadMgr.h"
#include "../util/JCCrypto.h"
#include "../util/JCIThreadCmdMgr.h"
#include "../downloadMgr/JCHttpHeader.h"
namespace laya
{
HandleFileData gHandleDataFunc = nullptr;
char* _conch_mallocData(int len) {
return new char[len];
}
void setFileDataHandler(HandleFileData h) {
gHandleDataFunc = h;
}
char* tttt(const char* data, int& len) {
//test setFileDataHandler
char* pd = _conch_mallocData(len);
memcpy(pd, data, len);
return pd;
}
std::string JCFileRes::s_strUploadChkErrUrl = "";
bool JCFileRes::s_bHasIgnoreChksum = false;
std::vector<std::string> JCFileRes::s_vIgnoreChksumError;
std::mutex JCFileRes::s_ignorechklock;
std::string JCFileRes::s_strExtVersion;
JCFileRes::JCFileRes(JCDownloadMgr* pNetLoader, JCFileResManager* pMgr)
{
m_nLastAction = INIT;
m_nLength = 0;
m_nLocalFileID = 0;
m_bDownloading = false;
m_CallbackRef.reset(new int(1));
m_bIgnoreError = false;
m_bSendToJS_complete = false;
m_pNetLoader = pNetLoader;
m_pMgr = pMgr;
}
JCFileRes::~JCFileRes() {
m_pBuffer.reset((char*)0);
m_CallbackRef.reset();
}
void JCFileRes::normalizeUrl() {
bool bToLower = m_pMgr ? m_pMgr->m_bUrlToLowerCase : false;
if (m_Url.m_nProto == JCUrl::file) {
std::string ret = m_Url.m_strPath + "/" + m_Url.m_vPath[m_Url.m_vPath.size() - 1];
if (bToLower)
UTF8ToLowercase((char*)ret.c_str());
m_strURL = ret;
return ;
}
std::string file = m_Url.m_vPath.size() > 0 ? m_Url.m_vPath[m_Url.m_vPath.size() - 1] : "";
std::string attFile = file.length() > 0 ? ("/" + file) : "/";
std::string& query = m_Url.m_Query;
std::string attQuery = query.length() > 0 ? (query) : "";
std::string ret = m_Url.m_strPath + attFile;
if (bToLower)
UTF8ToLowercase((char*)ret.c_str());
ret += attQuery;
m_strURL = ret;
}
void JCFileRes::load(const char* p_pszURL, JCSharedBuffer* pSyncResult) {
if (!p_pszURL)
return;
std::weak_ptr<int> wptr(m_CallbackRef);
m_Url.parse(p_pszURL);
normalizeUrl();
//bool bToLower = m_pMgr ? m_pMgr->m_bUrlToLowerCase : false;
//m_strURL = normalizePath(p_pszURL, bToLower, nProto);
//判断是否是file://
if (m_Url.m_nProto == JCUrl::file) {
std::weak_ptr<int> wptr(m_CallbackRef);
const char* pPath = m_strURL.c_str() + strlen("file://");
if (pPath[2] == ':') {
pPath += 1;
}
JCBuffer buf;
bool bret = readFileSync(pPath, buf);
if (bret && buf.m_pPtr) {
//如果本地打开成功,则可以立刻设置回调,因为是在一个线程
//这么写可以做到立即回调,相当于同步函数。这样即使没有缓存也可以应付 JsAppCache::loadCachedURL 的要求。
m_pBuffer.reset(new char[buf.m_nLen]);
memcpy(m_pBuffer.get(), buf.m_pPtr, buf.m_nLen);
m_nLength = buf.m_nLen;
//hugao add
checkIsEncrypted(m_pBuffer.get(), m_nLength);
if (gHandleDataFunc) {
int nNewlen = m_nLength;
char* pNewData = gHandleDataFunc(m_pBuffer.get(), nNewlen);
if (pNewData ) {
m_nLength = nNewlen;
m_pBuffer.reset(pNewData);
}
}
setState(ready);
if (pSyncResult) {
pSyncResult->m_pBuffer = m_pBuffer;
pSyncResult->m_nLen = m_nLength;
}
//立即失效。如果再有相同请求,需要重新加载
m_pBuffer.reset((char*)0); //TODO 测试:这个不一定会导致释放
m_nLength = 0;
setState(freed);
}
else {
onDownloadError(1, 0, wptr);
}
return;
}
unsigned int chksum = 0;
//再尝试从缓存中取
JCSharedBuffer buff;
bool needDownload = true;
JCServerFileCache* pSvFileCache = m_pMgr->m_pFileCache;
if (pSvFileCache) {
std::string strQuery = m_Url.m_Query;
std::string& extV = s_strExtVersion;
if (strQuery.length() > 1) {
if ( extV.length()>0 &&
strQuery.length() > extV.length() &&
memcmp(strQuery.c_str() + 1, extV.c_str(), extV.length()) == 0 &&
strQuery.find('&') == std::string::npos) {
m_bExtVersion = true;
//符合版本号的规则。这时候,只计算文件本身,?后面的都不算
std::string fullfile = m_Url.m_strPath + "/" + m_Url.m_vPath[m_Url.m_vPath.size() - 1];
m_nLocalFileID = pSvFileCache->getFileID(fullfile.c_str());
const char* pExtV = strQuery.c_str() + 1 + extV.length();
//不管是不是0都要计算真正的hash值,因为可能资源中没有,需要下载,需要缓存
m_nExtVersionID = JCCachedFileSys::hashRaw(pExtV);
if (*pExtV == '0'&& *(pExtV+1)==0) {
//版本为0的特殊处理
//前提是肯定有dcc
pSvFileCache->getFileInfo(m_nLocalFileID, chksum);
needDownload = !pSvFileCache->load(m_nLocalFileID, chksum, buff,false,true);
}
else {
JCCachedFileSys::typeChkSum tcs = (JCCachedFileSys::typeChkSum)m_nExtVersionID;
needDownload = !pSvFileCache->load(m_nLocalFileID, tcs, buff,true,true);
}
}
else {
//不是外部版本控制,
//20170821 ?? 这个要走缓存么? 已经忘了为什么这么写了。
// m_strURL是完整的带?的,如果缓存中通过某种方法加了这种地址,应该也允许缓存
m_nLocalFileID = pSvFileCache->getFileID(m_strURL.c_str());
JCCachedFileSys::typeChkSum tcs = 0;
needDownload = !pSvFileCache->load(m_nLocalFileID, tcs, buff, false,true);
}
}
else {
//没有?,可以用dcc
m_nLocalFileID = pSvFileCache->getFileID(m_strURL.c_str());// hashURLFull(m_strURL.c_str());
pSvFileCache->getFileInfo(m_nLocalFileID, chksum);
needDownload = !pSvFileCache->load(m_nLocalFileID, chksum, buff,false,true);
}
}
if (needDownload) {
if (m_bDownloading)
return;
//注意不能是p_pszURL。因为不可靠。如果是p_pszURL就是m_strURL获得的,则在 m_strURL = normalizePath
//的时候,会导致p_pszURL被释放。
m_bDownloading = true;
if (chksum != 0) {
char chkbuf[64] = { 0 };
sprintf(chkbuf, "%x", chksum);
std::string cdnfriendUrl = m_strURL + "?" + chkbuf;
verifyDownload(cdnfriendUrl.c_str(), chksum);
}
else {
verifyDownload(m_strURL.c_str(), chksum);
}
goto end;
}
//loadok:
{
m_nLastAction = LOADFROMCACHE;
//如果本地打开成功,则可以立刻设置回调,因为是在一个线程
m_pBuffer = buff.m_pBuffer;
m_nLength = buff.m_nLen;
if (pSyncResult) {
pSyncResult->m_pBuffer = buff.m_pBuffer;
pSyncResult->m_nLen = m_nLength;
}
//转到js线程执行setState。不要同步了,保持与浏览器一致。
if (m_pMgr->m_pCmdPoster) {
if (!m_bSendToJS_complete) {
m_bSendToJS_complete = true; //这里肯定是js线程,可以处理这个标志
std::function<void()> cb = std::bind(&JCFileRes::onResDownloadOK_JSThread, this, wptr);
m_pMgr->m_pCmdPoster->postToJS(cb);
}
}
else {
onResDownloadOK_JSThread(wptr);
}
}
end:
int a = 0;
}
bool JCFileRes::loadFromCache(JCBuffer& buff, bool bDoCheckSum) {
JCUrl url;
url.parse(m_strURL.c_str());
if (url.m_nProto == JCUrl::file) {
char* pFile = (char*)m_strURL.c_str();
if (strstr(m_strURL.c_str(), "file:///") == pFile) {
if (pFile[9] == ':') {
pFile += 8;
}
else {
pFile += 7;
}
}
return readFileSync(pFile, buff);
}
if (m_bDownloading)
return false;
if (!m_pMgr || !m_pMgr->m_pFileCache)
return false;
if (bDoCheckSum && m_bExtVersion) {
JCSharedBuffer _buff;
JCCachedFileSys::typeChkSum tcs = (JCCachedFileSys::typeChkSum)m_nExtVersionID;
if (m_pMgr->m_pFileCache->load(m_nLocalFileID, tcs, _buff, true, true)) {
if (_buff.m_pBuffer.get()) {
buff.create(_buff.m_nLen);
memcpy(buff.m_pPtr, _buff.m_pBuffer.get(), _buff.m_nLen);
return true;
}
}
return false;
}
return m_pMgr->m_pFileCache->load(m_nLocalFileID, buff, bDoCheckSum);
}
bool JCFileRes::checkIsEncrypted(char *buf, int len){
#ifndef THIN_COMMON_WITHOUT_DOWNLOAD
bool temp = JCEncrypt::decrypt(buf, len);
if (temp)m_nLength = len - JCEncrypt::s_nPreLen;
return temp;
#else
return false;
#endif
}
void JCFileRes::verifyDownload(const char* p_pszURL, unsigned int p_nChkSum) {
m_nLastAction = DOWNLOADING;
JCDownloadMgr* pNetLoader = m_pNetLoader;
std::weak_ptr<int> wptr(m_CallbackRef);
pNetLoader->download(p_pszURL, 0,
std::bind(&JCFileRes::onProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, wptr),
std::bind(&JCFileRes::onDownloaded, this,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3,
std::placeholders::_4,
std::placeholders::_5,
std::placeholders::_6,
p_nChkSum, 1, wptr), m_nOptTimeout, m_nConnTimeout);
}
void JCFileRes::onResDownloadOK_JSThread(std::weak_ptr<int> p_cbref) {
if (!p_cbref.lock())
return;
if (m_nLength == 0) //如果已经为0了,则表示已经处理的,状态改变已经通知给需要的人了,直接返回。
return;
//hugao add
checkIsEncrypted(m_pBuffer.get(), m_nLength);
if (gHandleDataFunc) {
int nNewlen = m_nLength;
char* pNewData = gHandleDataFunc(m_pBuffer.get(), nNewlen);
if (pNewData) {
m_nLength = nNewlen;
m_pBuffer.reset(pNewData);
}
}
setState(ready);
//立即失效。如果再有相同请求,需要重新加载
m_pBuffer.reset((char*)0); //TODO 测试:这个不一定会导致释放
m_nLength = 0;
setState(freed);
m_bSendToJS_complete = false; //处理完了,可以继续post了。
}
void JCFileRes::notifyErrorHandler(int p_nError, int p_nHttpResponse) {
if (!m_bIgnoreError) {
}
}
void JCFileRes::onResDownloadErr_JSThread(std::weak_ptr<int> p_cbref, int p_nError, int p_nHttpResponse) {
if (!p_cbref.lock())
return;
//mnErrNo = downloadError;
m_bDownloading = false;
m_nErrNo = p_nError;
m_nLastHttpResponse = p_nHttpResponse;
setState(error);
//立即失效。如果再有相同请求,需要重新加载
m_pBuffer.reset((char*)0); //TODO 测试:这个不一定会导致释放
m_nLength = 0;
//setState(freed); 不应该再设置成freed,应该保留error,否则再次设置 setOnErrorCB的话
}
inline bool _MaybeAnImg(const char* pBuff, int nLen) {
if (pBuff && nLen > 4) {
static int jpegID = 0x00ffd8ff; //最高位00那个位置不一定是什么
static int gifID = 0x38464947;
static int pngID = 0x474e5089;
int idval = *(int*)pBuff;
return (idval == pngID || idval == gifID || (idval & 0xffffff) == jpegID);
}
return false;
}
inline bool _StrInVec(const char* pStr, std::vector<std::string>& vec) {
for (int ei = 0, sz = vec.size(); ei < sz; ei++) {
if (vec[ei] == pStr) {
return true;
}
}
return false;
}
void JCFileRes::onDownloaded(JCBuffer& p_Buff,
const string& pLocalAddr, const string& pSvAddr,
int pnCurlRet, int pnHttpRet,
const string& pstrHeader,
unsigned int p_nChkSum,
int p_nDownloadNum, std::weak_ptr<int> p_cbref) {
//用这种方式并不可靠,当多线程的时候,可能在这时候还有效,执行完这句就被删除了。
if (!p_cbref.lock())
return;
m_nLastAction = DOWNLOADED;
m_strSvIP = pSvAddr;
LOGI("Downloaded %s@%s s=%x l=%d", m_strURL.c_str(), pSvAddr.c_str(), p_nChkSum, p_Buff.m_nLen);
bool chkErr = false;
JCServerFileCache* pSvFileCache = nullptr;
if (pnCurlRet == CURLE_OK && pnHttpRet >= 200 && pnHttpRet < 300) {
//如果什么都没有返回,则不用继续处理了
if (p_Buff.m_pPtr == NULL || p_Buff.m_nLen == 0)
goto end;
pSvFileCache = m_pMgr->m_pFileCache;
if (pSvFileCache) {
if (p_nChkSum > 0) {
unsigned int downchk = JCCachedFileSys::getChkSum(p_Buff.m_pPtr, p_Buff.m_nLen);
if (downchk != p_nChkSum) {
//校验没有通过。
if (s_strUploadChkErrUrl.length() > 0) {
//上传一次。注意这里必须是在下载线程,以免造成卡顿
JCDownloadMgr* pNetLoader = m_pNetLoader;
if (pNetLoader) {
char postbuf[1024];
sprintf(postbuf, "%s,%s,%08x,%08x,%d,%s",
pLocalAddr.c_str(),
pSvAddr.c_str(),
downchk, //实际
p_nChkSum, //应该
p_Buff.m_nLen,//长度
m_strURL.c_str());//url
pNetLoader->postData(s_strUploadChkErrUrl.c_str(),
postbuf, strlen(postbuf), JCDownloadMgr::defCompleteFunc
);
}
}
//这时候,只能算作临时文件
chkErr = true;
goto updatecache;
}
}
goto updatecache; //
}
//如果没有appcache也要通知onok
//转到js线程执行setState。
m_pBuffer = std::shared_ptr<char>(new char[p_Buff.m_nLen], std::default_delete<char[]>());
memcpy(m_pBuffer.get(), p_Buff.m_pPtr, p_Buff.m_nLen);
m_nLength = p_Buff.m_nLen;
std::weak_ptr<int> wptr(m_CallbackRef);
if (m_pMgr->m_pCmdPoster) {
std::function<void()> cb = std::bind(&JCFileRes::onResDownloadOK_JSThread, this, wptr);
m_pMgr->m_pCmdPoster->postToJS(cb);
}
else {
onResDownloadOK_JSThread(wptr);
}
goto end;
}
else {
//error
std::weak_ptr<int> wptr(m_CallbackRef);
onDownloadError(pnCurlRet, pnHttpRet, wptr);
goto end;
}
updatecache:{
std::string ext = getLowercaseExtOfUrl(m_strURL.c_str());
m_pBuffer = std::shared_ptr<char>(new char[p_Buff.m_nLen], std::default_delete<char[]>());
memcpy(m_pBuffer.get(), p_Buff.m_pPtr, p_Buff.m_nLen);
m_nLength = p_Buff.m_nLen;
std::weak_ptr<int> wptr(m_CallbackRef);
//需要更新缓存。
bool bNeedSave = false;
if (m_bExtVersion || (p_nChkSum != 0 && !chkErr)) {
//长期保存
std::string strLocalFile = pSvFileCache->updateAFile(m_nLocalFileID,
m_pBuffer.get(), p_Buff.m_nLen,
m_bExtVersion ? m_nExtVersionID : p_nChkSum,
m_bExtVersion,
0,0);
}
else if (_MaybeAnImg(p_Buff.m_pPtr, p_Buff.m_nLen) ||
_StrInVec(ext.c_str(), m_pMgr->m_vExtNeedSave) ||
chkErr ) {
//临时保存。进程内不允许删除
JCHttpHeader hh(pstrHeader.c_str());
//hh.getCacheContrl()
//TODO 计算过期时间
std::string strLocalFile = pSvFileCache->updateAFile(m_nLocalFileID,
m_pBuffer.get(), p_Buff.m_nLen,
0,
0,
0, 1);
}
else {
//临时保存,进程内可以删除
//TODO 需要实现临时缓存的管理
/*
std::string strLocalFile = pSvFileCache->updateAFile(m_nLocalFileID,
m_pBuffer.get(), p_Buff.m_nLen,
m_bExtVersion ? m_nExtVersionID : p_nChkSum,
m_bExtVersion,
0, 0);
*/
}
m_nLastAction = UPDATECACHE;
//转到js线程执行setState。
if (m_pMgr->m_pCmdPoster) {
std::function<void()> cb = std::bind(&JCFileRes::onResDownloadOK_JSThread, this, wptr);
m_pMgr->m_pCmdPoster->postToJS(cb);
}
else {
onResDownloadOK_JSThread(wptr);
}
}
//nocache:
//int b = 0;
end:
//int c = 0;
m_bDownloading = false;
}
//问题:这个可能是在另外的线程调用的
void JCFileRes::onDownloadedOld(JCBuffer& p_Buff,
const string& pLocalAddr, const string& pSvAddr,
int pnCurlRet, int pnHttpRet,
const string& pstrHeader,
unsigned int p_nChkSum,
int p_nDownloadNum, std::weak_ptr<int> p_cbref) {
//用这种方式并不可靠,当多线程的时候,可能在这时候还有效,执行完这句就被删除了。
if (!p_cbref.lock())
return;
m_nLastAction = DOWNLOADED;
m_strSvIP = pSvAddr;
LOGI("Downloaded %s@%s s=%x l=%d", m_strURL.c_str(), pSvAddr.c_str(), p_nChkSum, p_Buff.m_nLen);
bool downError = (p_Buff.m_pPtr == NULL || p_Buff.m_nLen == 0);
std::string ext = getLowercaseExtOfUrl(m_strURL.c_str());
JCServerFileCache* pSvFileCache = m_pMgr->m_pFileCache;
if (!downError) {
//先写到fileCache中。这时候还不是js线程,这里写是为了不影响js线程的效率。
//问题是可能导致load和写cache不在一个线程。这个可能问题不大
//如果非要一个线程,则这里不要调用,通过 setOnReadyCB( std::bind(&svFileCache::onFileDownloaded, pSvFileCache, std::placeholders::_1, chksum)) 的方法在js线程中执行
bool chkok = p_nChkSum == 0;
if (p_nChkSum) {
unsigned int downchk = 0;
//如果是文本文件,把\r\n统一换成\n再进行校验,以绕开各个环节的转换
downchk = JCCachedFileSys::getChkSum(p_Buff.m_pPtr, p_Buff.m_nLen);
chkok = (downchk == p_nChkSum);
if (!chkok && checkIgnoreChksum((char*)ext.c_str())) {
LOGW("[%s@%s]Check error,but you can ignore,real:%08x,should be:%08x,loc=%s\n", m_strURL.c_str(), pSvAddr.c_str(),
downchk, p_nChkSum, pLocalAddr.c_str());
//errorflog("[%s@%s]校验错误,但忽略,实%08x,应 %08x,loc=%s\n", m_strURL.c_str(), p_pCurl->m_strSvAddr.c_str(), downchk, p_nChkSum, p_pCurl->m_strLocalAddr.c_str());
chkok = true; //算作正确
//上传一次
if (s_strUploadChkErrUrl.length() > 0) {
//上传一次。注意这里必须是在下载线程,以免造成卡顿
JCDownloadMgr* pNetLoader = m_pNetLoader;
if (pNetLoader) {
char postbuf[1024];
sprintf(postbuf, "%s,%s,%08x,%08x,%d,%s",
pLocalAddr.c_str(),
pSvAddr.c_str(),
downchk, //实际
p_nChkSum, //应该
p_Buff.m_nLen,//长度
m_strURL.c_str());//url
pNetLoader->postData(s_strUploadChkErrUrl.c_str(),
postbuf, strlen(postbuf), JCDownloadMgr::defCompleteFunc
);
}
}
}
if (!chkok) {
LOGW("[%s@%s]check error,real:%08x,should be:%08x,loc=%s\n", m_strURL.c_str(), pSvAddr.c_str(), downchk,
p_nChkSum, pLocalAddr.c_str());
//errorflog("[%s@%s]校验错误,实%08x,应 %08x,loc=%s\n", m_strURL.c_str(), p_pCurl->m_strSvAddr.c_str(), downchk, p_nChkSum, p_pCurl->m_strLocalAddr.c_str());
if (p_nDownloadNum >= MAXDOWNLOADTRY && s_strUploadChkErrUrl.length() > 0) {
//上传一次。注意这里必须是在下载线程,以免造成卡顿
JCDownloadMgr* pNetLoader = m_pNetLoader;
if (pNetLoader) {
char postbuf[1024];
sprintf(postbuf, "%s,%s,%08x,%08x,%d,%s",
pLocalAddr.c_str(),
pSvAddr.c_str(),
downchk, //实际
p_nChkSum, //应该
p_Buff.m_nLen,//长度
m_strURL.c_str());//url
pNetLoader->postData(s_strUploadChkErrUrl.c_str(), postbuf, strlen(postbuf),
JCDownloadMgr::defCompleteFunc
);
}
}
}
}
if (!chkok && p_nDownloadNum < MAXDOWNLOADTRY) {
//继续下载
LOGI("再次尝试下载");
JCDownloadMgr* pNetLoader = m_pNetLoader;
std::weak_ptr<int> wptr(m_CallbackRef);
pNetLoader->download(m_strURL.c_str(), 0,
std::bind(&JCFileRes::onProgress, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, wptr),
std::bind(&JCFileRes::onDownloaded, this,
std::placeholders::_1,
std::placeholders::_2,
std::placeholders::_3,
std::placeholders::_4,
std::placeholders::_5,
std::placeholders::_6,
p_nChkSum, ++p_nDownloadNum, wptr),
0);
return;
}
downError = !chkok;
if (!chkok && s_strUploadChkErrUrl.length() <= 0) {//如果有上传地址,就不再需要提示了
//JCErrorHandleInterface* pEH = getErrorHandler();
//if(pEH){
// pEH->download_chkSumVerifyError(m_strURL.c_str());
//}
}
}
//下载的数据先copy一份,使用完了之后,立即删除
//即使校验错误,也算作正确的。
if (!downError || p_nDownloadNum > 2) {
//注意:这里并没有复制buffer,外部使用者需要自己复制。
m_pBuffer = std::shared_ptr<char>(new char[p_Buff.m_nLen],std::default_delete<char[]>());
memcpy(m_pBuffer.get(), p_Buff.m_pPtr, p_Buff.m_nLen);
m_nLength = p_Buff.m_nLen;
std::weak_ptr<int> wptr(m_CallbackRef);
//如果是缓存文件。需要更新缓存。
bool bNeedSave = false;
if (p_nChkSum) {
bNeedSave = p_nDownloadNum <= 1;
}
else {
//如果是图片文件要恢复
if (p_Buff.m_pPtr && p_Buff.m_nLen > 4) {
static int jpegID = 0x00ffd8ff; //最高位00那个位置不一定是什么
static int gifID = 0x38464947;
static int pngID = 0x474e5089;
int idval = *(int*)p_Buff.m_pPtr;
if (idval == pngID || idval == gifID || (idval & 0xffffff) == jpegID)
bNeedSave = true;
}
//如果是外部版本管理,需要保存
if (!bNeedSave) {
}
if (!bNeedSave) {
//如果是需要同步恢复的资源文件,就要缓存
for (int ei = 0, sz = m_pMgr->m_vExtNeedSave.size(); ei < sz; ei++) {
if (m_pMgr->m_vExtNeedSave[ei] == ext) {
bNeedSave = true;
break;
}
}
}
}
if (bNeedSave){
std::string strLocalFile = pSvFileCache->updateAFile(
m_nLocalFileID, m_pBuffer.get(), p_Buff.m_nLen,
p_nChkSum, 0, 0, 0);
if (strLocalFile.length() > 0) {
m_nLastAction = UPDATECACHE;
}
}
//转到js线程执行setState。
if (m_pMgr->m_pCmdPoster) {
std::function<void()> cb = std::bind(&JCFileRes::onResDownloadOK_JSThread, this, wptr);
m_pMgr->m_pCmdPoster->postToJS(cb);
}
else {
onResDownloadOK_JSThread(wptr);
}
}
else {
m_pBuffer.reset((char*)0);
m_nLength = 0;
//notifyErrorHandler(Curl::EC_PartialFile, 0); //校验错误的responsecode只能0了
std::weak_ptr<int> wptr(m_CallbackRef);
if (m_pMgr->m_pCmdPoster) {
//转到js线程执行setState。
std::function<void()> cb = std::bind(&JCFileRes::onResDownloadErr_JSThread,
this, wptr, -1, 0);
m_pMgr->m_pCmdPoster->postToJS (cb);
}
else {
onResDownloadErr_JSThread(wptr, -1, 0);
}
}
m_bDownloading = false;
}
bool JCFileRes::restoreRes() {
load(m_strURL.c_str(),nullptr);
return true;
}
void JCFileRes::onDownloadError(int p_nError, int p_nHttpResponse, std::weak_ptr<int> p_cbref) {
//std::lock_guard<std::mutex> lock(m_pMgr->m_maplock);//要删除map,可能会与请求的时候冲突
if (!p_cbref.lock())
return;
m_pBuffer.reset((char*)0);
m_nLength = 0;
if (!m_bIgnoreError) {
LOGE("JCFileRes::onDownloadError file error[%d]:%s", p_nError, m_strURL.c_str());
//errorflog("下载文件错误[%d]%s",p_nError, m_strURL.c_str());
}
//m_pMgr->delRes(m_strURL.c_str()); 到资源管理器中统一做
//auto it = m_ResMap.find(m_strURL.c_str());
notifyErrorHandler(p_nError, p_nHttpResponse);
std::weak_ptr<int> wptr(m_CallbackRef);
//转到js线程执行setState。
if (m_pMgr->m_pCmdPoster) {
std::function<void()> cb = std::bind(&JCFileRes::onResDownloadErr_JSThread, this, wptr, p_nError, p_nHttpResponse);
m_pMgr->m_pCmdPoster->postToJS(cb);
}
else {
onResDownloadErr_JSThread(wptr, p_nError, p_nHttpResponse);
}
}
int JCFileRes::onProgress(unsigned int now, unsigned int total, float speed, std::weak_ptr<int> p_cbref) {
return 0;
}
JCFileResManager::JCFileResManager(JCDownloadMgr* pDownloadMgr) {
m_pFileCache = nullptr;
m_pCmdPoster = nullptr;
m_bUrlToLowerCase = false;
m_vExtNeedSave = {".png",".jpg",".wav",".ogg"};
m_pDownloadMgr = pDownloadMgr;
}
JCFileResManager::~JCFileResManager() {
clear();
}
void JCFileResManager::clear() {
std::lock_guard<std::mutex> lock(m_maplock);
FileResMap::iterator it = m_ResMap.begin();
FileResMap::iterator ed = m_ResMap.end();
while (it != ed) {
delete (*it).second;
it++;
}
m_ResMap.clear();
}
//如果已经下载完了,应该同步去打开文件,不要再走下载了(在load中做了)
JCFileRes* JCFileResManager::getRes(const std::string& url, int p_nConnTimeout , int p_nOptTimeout) {
std::lock_guard<std::mutex> lock(m_maplock);//下面要添加资源,需要锁。因为失败的话会删除
JCFileRes* pRes = NULL;
FileResMap::iterator it = m_ResMap.find(url);
if (it == m_ResMap.end()) {
pRes = new JCFileRes(m_pDownloadMgr,this);
if (p_nConnTimeout>0) {
pRes->m_nConnTimeout = p_nConnTimeout;
}
if (p_nOptTimeout > 0) {
pRes->m_nOptTimeout = p_nOptTimeout;
}
m_ResMap[url] = pRes;
//问题:load和下载回调不在一个线程会有问题么?
// 如果load不修改表和文件内容,下载回调在最后再设置ready,应该没事
pRes->load(url.c_str(),nullptr);
//m_pThread = workerThread::getCurThread();
return pRes;
}
pRes = (*it).second;
if (p_nConnTimeout>0) {
pRes->m_nConnTimeout = p_nConnTimeout;
}
if (p_nOptTimeout > 0) {
pRes->m_nOptTimeout = p_nOptTimeout;
}
return pRes;
}
bool JCFileResManager::delRes(const char* p_pszURL) {
std::lock_guard<std::mutex> lock(m_maplock);
auto it = m_ResMap.find(p_pszURL);
if (it != m_ResMap.end()) {
m_ResMap.erase(it);
return true;
}
return false;
}
void JCFileRes::addChkIgnoreChksumExt(const char* p_pszExt) {
s_bHasIgnoreChksum = true;
std::string ext = p_pszExt;
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
s_ignorechklock.lock();
for (int i = 0, sz = s_vIgnoreChksumError.size(); i < sz; i++) {
if (s_vIgnoreChksumError[i] == ext) {
s_ignorechklock.unlock();
return;
}
}
s_vIgnoreChksumError.push_back(ext);
s_ignorechklock.unlock();
}
void JCFileRes::clearChkIgnoreChksumExt() {
s_bHasIgnoreChksum = false;
s_ignorechklock.lock();
s_vIgnoreChksumError.clear();
s_ignorechklock.unlock();
}
bool JCFileRes::checkIgnoreChksum(char* p_pszExt) {
if (!s_bHasIgnoreChksum)
return false;
std::string ext = p_pszExt;
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
s_ignorechklock.lock();
for (int i = 0, sz = s_vIgnoreChksumError.size(); i < sz; i++) {
if (s_vIgnoreChksumError[i] == ext) {
s_ignorechklock.unlock();
return true;
}
}
s_ignorechklock.unlock();
return false;
}
}
//------------------------------------------------------------------------------
//-----------------------------END FILE--------------------------------