using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Networking;
public class ABUpdateMgr : MonoBehaviour
{
//AB包下载管理器脚本
private static ABUpdateMgr instance;
public static ABUpdateMgr Instance
{
get
{
if (instance == null)
{
GameObject obj = new GameObject("ABUpdateMgr");//为空则创建单例,自动添加到场景中
instance = obj.AddComponent<ABUpdateMgr>();
}
return instance;
}
}
//存储远端AB包信息字典,后续与本地对比
private Dictionary<string, ABInfo> remoteABInfo = new Dictionary<string, ABInfo>();
//用于存储本地AB包信息字典
private Dictionary<string, ABInfo> localABInfo = new Dictionary<string, ABInfo>();
//待下载的AB包文件名
private List<string> downLoadList = new List<string>();
public void CheckUpdate(UnityAction<bool> overCallBack, UnityAction<string> updateInfoCallBack)
{
//加载远端对比文件
DownLoadABCompareFile((isOver) =>
{
updateInfoCallBack("开始更新资源");
if (isOver)
{
updateInfoCallBack("对比文件下载完成");
string remoteInfo = File.ReadAllText(Application.persistentDataPath + "/ABCompareInfo_TMP.txt");
updateInfoCallBack("解析远端对比文件");
GetRemoteABCompareFileInfo(remoteInfo, remoteABInfo);
updateInfoCallBack("解析远端对比文件完成");
//加载本地对比文件
GetLocalABCompareFileInfo((isOver) =>
{
if (isOver)
{
updateInfoCallBack("解析本地对比文件完成");
updateInfoCallBack("开始对比");
//对比进行AB包下载
foreach (string abName in remoteABInfo.Keys)
{
if (!localABInfo.ContainsKey(abName))
{
//新资源下载
downLoadList.Add(abName);
}
else
{
//资源替换
if (localABInfo[abName].md5 != remoteABInfo[abName].md5)
downLoadList.Add(abName);
//本地移除
localABInfo.Remove(abName);
}
}
updateInfoCallBack("对比完成");
updateInfoCallBack("删除无用AB包文件");
//对比结束 删除没用内容 再下载AB包
foreach (string abName in localABInfo.Keys)
{
//如果可读写文件夹中有内容则删除
if (File.Exists(Application.persistentDataPath + "/" + abName))
File.Delete(Application.persistentDataPath + "/" + abName);
}
updateInfoCallBack("下载更新AB包文件");
//下载待更新列表中的所有AB包
DownLoadABFile((isOver) =>
{
if (isOver)
{
//把之前读取的远端对比文件信息存储到本地
updateInfoCallBack("更新本地AB包对比文件完成");
File.WriteAllText(Application.persistentDataPath + "/ABCompareInfo.txt", remoteInfo);
//更新本地AB包对比文件
}
overCallBack(isOver);
}, updateInfoCallBack);
}
else
overCallBack(false);
});
}
else
{
overCallBack(false);
}
});
}
/// <summary>
/// 下载AB包对比文件
/// </summary>
/// <param name="overCallback"></param>
public async void DownLoadABCompareFile(UnityAction<bool> overCallback)
{
//从资源服务器下载资源对比文件
//www UnityWebRequest 适合http服务器,更简单
//Debug.Log(Application.persistentDataPath);
bool isOver = false;
int reDownLoadMaxNum = 5;
//本地存储路径
string localPath = Application.persistentDataPath + "/";
while (!isOver && reDownLoadMaxNum > 0)
{
await Task.Run(() =>
{
isOver = DownLoadFile("ABCompareInfo.txt", localPath + "ABCompareInfo_TMP.txt");
});
--reDownLoadMaxNum;
}
//告诉外部是否成功
overCallback?.Invoke(isOver);//传空不执行
}
/// <summary>
/// 获取下载下来的AB包信息
/// </summary>
public void GetRemoteABCompareFileInfo(string info, Dictionary<string, ABInfo> ABInfo)
{
//根据资源对比文件的字符串进行拆分,TMP临时文件
// string info = File.ReadAllText(Application.persistentDataPath + "/ABCompareInfo_TMP.txt");
string[] strs = info.Split('|');
string[] infos = null;
for (int i = 0; i < strs.Length; i++)
{
//名字 大小 MD5码
infos = strs[i].Split(' ', ' ');
//存储每一个远端AB包信息,后面对比
ABInfo.Add(infos[0], new ABInfo(infos[0], infos[1], infos[2]));
}
//Debug.Log("AB包对比文件内容获取结束");
}
/// <summary>
/// 本地AB包对比文件加载和解析
/// </summary>
public void GetLocalABCompareFileInfo(UnityAction<bool> overCallBack)
{
//如果可读可写文件夹中有对比文件
if (File.Exists(Application.persistentDataPath + "/ABCompareInfo.txt"))
{
StartCoroutine(GetLocalABCompareFileInfo(Application.persistentDataPath + "/ABCompareInfo.txt", overCallBack));
}
//可读可写文件没有对比文件时,加载默认资源(第一次启动)
else if (File.Exists(Application.streamingAssetsPath + "/ABCompareInfo.txt"))
{
StartCoroutine(GetLocalABCompareFileInfo(Application.streamingAssetsPath + "/ABCompareInfo.txt", overCallBack));
}
else
{
overCallBack(true);
}
//如果两个文件都没有,表示第一次启动且没有默认资源
}
/// <summary>
/// 加载本地信息存入字典
/// </summary>
/// <param name="filePath"></param>
/// <returns></returns>
private IEnumerator GetLocalABCompareFileInfo(string filePath, UnityAction<bool> overCallBack)
{
//加载本地文件
UnityWebRequest req = UnityWebRequest.Get(filePath);
yield return req.SendWebRequest();
//加载成功
if (req.result == UnityWebRequest.Result.Success)
{
GetRemoteABCompareFileInfo(req.downloadHandler.text, localABInfo);
//Debug.Log(req.downloadHandler.text);
overCallBack(true);
}
else
{
overCallBack(false);
}
}
public async void DownLoadABFile(UnityAction<bool> overCallBack, UnityAction<string> updatePro)
{
//遍历字典的键 根据文件名下载AB包
/* foreach (string name in remoteABInfo.Keys)
{
//直接放入待下载列表
//-----------------与对比文件对比排除不变文件
downLoadList.Add(name);
}*/
//本地存储路径
string localPath = Application.persistentDataPath + "/";
//是否下载成功
bool isOver;
//下载成功列表
List<string> _list = new List<string>();
//重新下载最大次数
int reDownLoadMaxNum = 5;
//下载成功资源数
int downLoadOverNum = 0;
//该次需下载资源数
int dowmLoadMaxNum = downLoadList.Count;
//循环目的 进行多次循环下载,避免网络异常下载失败
while (downLoadList.Count > 0 && reDownLoadMaxNum > 0)
{
for (int i = 0; i < downLoadList.Count; i++)
{
isOver = false;
await Task.Run(() =>
{
isOver = DownLoadFile(downLoadList[i], localPath + downLoadList[i]);
});
if (isOver)
{
//进度
updatePro(++downLoadOverNum + "/" + dowmLoadMaxNum);
//Debug.Log("下载进度:" + (++downLoadOverNum) + "/" + downLoadList.Count);
_list.Add(downLoadList[i]);//下载成功则记录
}
}
//把下载的文件名从待下载列表移除
for (int i = 0; i < _list.Count; i++)
{
downLoadList.Remove(_list[i]);
}
--reDownLoadMaxNum;
}
//所有文件下载完成,委托告诉是否外部下载完成
overCallBack(downLoadList.Count == 0);
}
private bool DownLoadFile(string flieName, string localPath)
{
try
{
//创建Ftp链接下载
FtpWebRequest req = FtpWebRequest.Create(new Uri("ftp://192.168.1.103/AB/PC/" + flieName)) as FtpWebRequest;
//设置通信凭证下载(如果有匿名账号可不设置凭证)
NetworkCredential n = new NetworkCredential("Box", "jj13579");//账号密码
req.Credentials = n;
//设置代理为null,请求完毕后关闭控制连接,操作命令-上传,指定传输类型-2进制
req.Proxy = null;
req.KeepAlive = false;
//操作命令,下载
req.Method = WebRequestMethods.Ftp.DownloadFile;
req.UseBinary = true;
//下载文件 创建流对象
FtpWebResponse res = req.GetResponse() as FtpWebResponse;
Stream downLoadStream = res.GetResponseStream();
//读取文件信息,写入流对象
using (FileStream file = File.Create(localPath))
{
//2kb下载
byte[] bytes = new byte[2048];
int contentLength = downLoadStream.Read(bytes, 0, bytes.Length);//读到内容长度
//循环下载文件数据
while (contentLength != 0)
{
//写入到本地文件流中
file.Write(bytes, 0, contentLength);
//写完再读,为0为止
contentLength = downLoadStream.Read(bytes, 0, bytes.Length);
}
//循环完毕 下载结束
file.Close();
downLoadStream.Close();
}
return true;
}
catch (Exception ex)
{
Debug.Log(flieName + "下载失败:" + ex);
return false;
}
}
private void OnDestroy()
{
instance = null;
}
public class ABInfo//AB包信息
{
public string name;//名字
public long size;//大小
public string md5;//MD5码
public ABInfo(string name, string size, string md5)//构造函数
{
this.name = name;
this.size = long.Parse(size);
this.md5 = md5;
}
}
}
使用
ABUpdateMgr.Instance.CheckUpdate((isOver) =>
{
if (isOver)
Debug.Log("检测更新完成");
else
Debug.Log("检测更新失败,请检测网络");
}, (str) =>
{
Debug.Log(str);
});