特別感謝: Deepsky 幫忙解答
♫⊹香草奶昔⌒ 提供話題在cocos2dx中,提供了CCFileUitl來進行文件操作,但是大家一般習慣的方式還是僅僅通過這個類獲取路徑,然後用fopen相關的函數來操作,大概如下:
string fullPath = fullPathForFilename(pszFileName);
FILE *fp = fopen(fullPath.c_str(), pszMode);
但是這樣,windows和iOS正常無誤,但是在android下面,會讀取不到文件。之前遇到這個問題,沒有解決,後面爲了統一,改成了CCFileUtil的getFileData就沒有出現過,也沒有繼續去追。
今天追了之後,發現,在android下,CCFileUtil有獨立實現,在cocos2d-x-2.2.3/cocos2dx/platform/android/下面(引擎版本2.2.3),CCFileUtilsAndroid。其中getFileData的實現如下:
unsigned char* CCFileUtilsAndroid::getFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize)
{
return doGetFileData(pszFileName, pszMode, pSize, false);
}
unsigned char* CCFileUtilsAndroid::getFileDataForAsync(const char* pszFileName, const char* pszMode, unsigned long * pSize)
{
return doGetFileData(pszFileName, pszMode, pSize, true);
}
unsigned char* CCFileUtilsAndroid::doGetFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize, bool forAsync)
{
unsigned char * pData = 0;
if ((! pszFileName) || (! pszMode) || 0 == strlen(pszFileName))
{
return 0;
}
string fullPath = fullPathForFilename(pszFileName);
if (fullPath[0] != '/')
{
if (forAsync)
{ /********** !!!注意啊 ***********/
pData = s_pZipFile->getFileData(fullPath.c_str(), pSize, s_pZipFile->_dataThread);
}
else
{
pData = s_pZipFile->getFileData(fullPath.c_str(), pSize);
}
}
else
{
do
{
FILE *fp = fopen(fullPath.c_str(), pszMode);
CC_BREAK_IF(!fp);
unsigned long size;
fseek(fp,0,SEEK_END);
size = ftell(fp);
fseek(fp,0,SEEK_SET);
pData = new unsigned char[size];
size = fread(pData,sizeof(unsigned char), size,fp);
fclose(fp);
if (pSize)
{
*pSize = size;
}
} while (0);
}
if (! pData)
{
std::string msg = "Get data from file(";
msg.append(pszFileName).append(") failed!");
CCLOG("%s", msg.c_str());
}
return pData;
}
注意上面代碼裏面我標記註釋的地方,有個東西叫s_pZipFile,之前看到這裏,沒注意這個東西。仔細一看,前面的fullpath的返回值,是一個相對於asset/的文件,也就是說,基本上都會走這個if,那麼這個s_pZipFile又是怎麼定義的。
#include "support/zip_support/ZipUtils.h"
// record the zip on the resource path
static ZipFile *s_pZipFile = NULL;
CCFileUtils* CCFileUtils::sharedFileUtils()
{
if (s_sharedFileUtils == NULL)
{
s_sharedFileUtils = new CCFileUtilsAndroid();
s_sharedFileUtils->init();
std::string resourcePath = getApkPath();
s_pZipFile = new ZipFile(resourcePath, "assets/");
}
return s_sharedFileUtils;
}
重要的頭文件我都留在上面了,所以其實上面那個getFileData的實現是這樣的
unsigned char *ZipFile::getFileData(const std::string &fileName, unsigned long *pSize, ZipFilePrivate *data)
{
unsigned char * pBuffer = NULL;
if (pSize)
{
*pSize = 0;
}
do
{
CC_BREAK_IF(!data->zipFile);
CC_BREAK_IF(fileName.empty());
ZipFilePrivate::FileListContainer::const_iterator it = data->fileList.find(fileName);
CC_BREAK_IF(it == data->fileList.end());
ZipEntryInfo fileInfo = it->second;
int nRet = unzGoToFilePos(data->zipFile, &fileInfo.pos);
CC_BREAK_IF(UNZ_OK != nRet);
nRet = unzOpenCurrentFile(data->zipFile);
CC_BREAK_IF(UNZ_OK != nRet);
pBuffer = new unsigned char[fileInfo.uncompressed_size];
int CC_UNUSED nSize = unzReadCurrentFile(data->zipFile, pBuffer, fileInfo.uncompressed_size);
CCAssert(nSize == 0 || nSize == (int)fileInfo.uncompressed_size, "the file size is wrong");
if (pSize)
{
*pSize = fileInfo.uncompressed_size;
}
unzCloseCurrentFile(data->zipFile);
} while (0);
return pBuffer;
}
基本上到這,問題就清楚了,總結起來就是2點:
一、assets其實是一個zip壓縮文件,直接讀取裏面的內容是不行的。
二、android的實現和其他2個平臺不一樣。
解決辦法:
最簡單的,直接用CCFileUtil的getFileData實現文件讀取。
不然就從上面提到的文件去改。。。
其他方法,參考:1、 http://www.cppblog.com/johndragon/archive/2012/12/28/196754.html
2、 http://blog.csdn.net/happyhell/article/details/7414110