由于项目已实现热更新,所以资源优先由沙盒读取,或完全直接从沙盒读取。为了防止在更新大版本时使用到旧项目的资源,所以需要对沙盒中旧的资源进行覆盖。
暂时实现了以下两种copy方案。
MD5文件格式与生成:
方法一:将本地资源一次性全部copy到沙盒,仅在首次安装或覆盖安装时对比oldfiles进行筛选覆盖
缺点:很慢。 30m资源耗时近20s,可优化:文件压缩后copy过去再解压。该方案暂时不用,仅做参考
private string oldfilesName = "oldfiles.txt";
private string filesName = "files.txt";
IEnumerator CopyFolder()
{
List<FileInfo> localInfos = new List<FileInfo>();
List<FileInfo> shaheInfos = new List<FileInfo>();
List<FileInfo> copyInfos = new List<FileInfo>();
//本地oldfiles
string src = getStreamingPath_for_www() + oldfilesName;
WWW www = new WWW(src);
yield return www;
if (!string.IsNullOrEmpty(www.error))
{
Debug.Log("www.error:" + www.error);
}
else
{
StringReader reader = new StringReader(www.text);
string line;
while ((line = reader.ReadLine()) != null)
{
string[] temp = line.Split(‘|‘);
string fileName = temp[0];
string md5 = temp[1];
int length = int.Parse(temp[2]);
FileInfo info;
info.fileName = fileName;
info.md5 = md5;
info.length = length;
localInfos.Add(info);
}
reader.Dispose();
reader.Close();
}
www.Dispose();
if (localInfos.Count > 0)
{
//沙盒oldfiles
string des = Application.persistentDataPath + "/" + oldfilesName;
if (File.Exists(des))
{
StreamReader reader = File.OpenText(des);
string line;
while ((line = reader.ReadLine()) != null)
{
string[] temp = line.Split(‘|‘);
string fileName = temp[0];
string md5 = temp[1];
int length = int.Parse(temp[2]);
FileInfo info;
info.fileName = fileName;
info.md5 = md5;
info.length = length;
shaheInfos.Add(info);
}
reader.Dispose();
reader.Close();
}
//需要更新oldfiles
copyInfos = CompareFileInfo(localInfos, shaheInfos);
foreach (var item in copyInfos)
{
//string mfileName = item.fileName.Replace("\\","/");
string _src = getStreamingPath_for_www() + item.fileName;
string fileName = item.fileName.Substring(item.fileName.LastIndexOf(‘/‘) + 1);
string _des = Application.persistentDataPath + "/" + fileName;
//Debug.Log("des:" + _des);
//Debug.Log("src:" + _src);
WWW _www = new WWW(_src);
yield return _www;
if (!string.IsNullOrEmpty(_www.error))
{
Debug.Log("_www.error:" + _www.error);
}
else
{
FileStream stream = new FileStream(_des, FileMode.Create);
stream.Write(_www.bytes, 0, _www.bytes.Length);
stream.Flush();
stream.Close();
}
_www.Dispose();
}
if (copyInfos.Count > 0)
{
//copy oldfiles
StartCoroutine(copy(oldfilesName));
//删除 file 和 version
//删除这两个文件是copy完之后让游戏重新进行一次热更新,以保证沙盒资 源与资源服资源保持一致(也是避免新资源被copy而报错)
string filepath = Application.persistentDataPath + "/files.txt";
string versionpath = Application.persistentDataPath + "/Version";
if (File.Exists(filepath))
{
File.Delete(filepath);
}
if (File.Exists(versionpath))
{
File.Delete(versionpath);
}
}
Debug.logger.Log("文件复制完成:共" + copyInfos.Count + "个文件");
}
else
{
Debug.logger.Log("本地无文件:" + copyInfos.Count + "个文件");
}
StartCoroutine(compareVersion());
}
/// <summary>
/// 将streaming path 下的文件copy到对应用
///
IEnumerator copy(string fileName)
{
string src = getStreamingPath_for_www() + fileName;
string des = Application.persistentDataPath + "/" + fileName;
//Debug.Log("des:" + des);
//Debug.Log("src:" + src);
WWW www = new WWW(src);
yield return www;
if (!string.IsNullOrEmpty(www.error))
{
Debug.Log("www.error:" + www.error);
}
else
{
if (File.Exists(des))
{
File.Delete(des);
}
FileStream fsDes = File.Create(des);
fsDes.Write(www.bytes, 0, www.bytes.Length);
fsDes.Flush();
fsDes.Close();
}
www.Dispose();
}
//比较md5
List<FileInfo> CompareFileInfo(List<FileInfo> localInfos, List<FileInfo> shaheInfos)
{
List<FileInfo> downloadList = new List<FileInfo>();
for (int i = 0; i < localInfos.Count; i++)
{
FileInfo info = localInfos[i];
bool flag = NeedCopy(info.fileName, info.md5, shaheInfos);
if (flag)
{
downloadList.Add(info);
}
}
return downloadList;
}
//true 需要下载,false 不用下载
bool NeedCopy(string fileName, string md5, List<FileInfo> shaheInfos)
{
for (int i = 0; i < shaheInfos.Count; i++)
{
if (shaheInfos[i].fileName == fileName)
{
if (shaheInfos[i].md5 == md5)
{
return false;
}
else
{
return true;
}
}
}
return true;
}
string getStreamingPath_for_www()
{
string pre = "file://";
#if UNITY_EDITOR
pre = "file://";
#elif UNITY_ANDROID
pre = "";
#elif UNITY_IPHONE
pre = "file://";
#endif
string path = pre + Application.streamingAssetsPath + "/";
return path;
}
string getPersistentPath_for_www()
{
string pre = "file://";
#if UNITY_EDITOR || UNITY_STANDALONE_WIN
pre = "file:///";
#elif UNITY_ANDROID
pre = "file://";
#elif UNITY_IPHONE
pre = "file://";
#endif
string path = pre + Application.persistentDataPath + "/";
return path;
}
#endregion
方法二、仅copy覆盖沙盒中存在的旧资源