/** @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 #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 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 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 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 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 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 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 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& 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 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(new char[p_Buff.m_nLen], std::default_delete()); memcpy(m_pBuffer.get(), p_Buff.m_pPtr, p_Buff.m_nLen); m_nLength = p_Buff.m_nLen; std::weak_ptr wptr(m_CallbackRef); if (m_pMgr->m_pCmdPoster) { std::function 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 wptr(m_CallbackRef); onDownloadError(pnCurlRet, pnHttpRet, wptr); goto end; } updatecache:{ std::string ext = getLowercaseExtOfUrl(m_strURL.c_str()); m_pBuffer = std::shared_ptr(new char[p_Buff.m_nLen], std::default_delete()); memcpy(m_pBuffer.get(), p_Buff.m_pPtr, p_Buff.m_nLen); m_nLength = p_Buff.m_nLen; std::weak_ptr 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 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 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 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(new char[p_Buff.m_nLen],std::default_delete()); memcpy(m_pBuffer.get(), p_Buff.m_pPtr, p_Buff.m_nLen); m_nLength = p_Buff.m_nLen; std::weak_ptr 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 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 wptr(m_CallbackRef); if (m_pMgr->m_pCmdPoster) { //转到js线程执行setState。 std::function 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 p_cbref) { //std::lock_guard 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 wptr(m_CallbackRef); //转到js线程执行setState。 if (m_pMgr->m_pCmdPoster) { std::function 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 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 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 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 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--------------------------------