797 lines
33 KiB
C++
797 lines
33 KiB
C++
/**
|
||
@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--------------------------------
|