如果发布android端 需要将路径改为Application.persistentDataPath
还有一定要注意ip地址和打包的平台类型..... 一上午买了个记性.....
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using UnityEngine.Networking;
public class ResUpdate : MonoBehaviour
{
public string VERSION_FILE;// = "version.txt"; //文件名
public string LOCAL_RES_URL;// = "file://" + Application.dataPath + "/Res/"; //本地文件的路径加file是为了使用WWW拿到绝对路径,这个例子其实可以只使用下边一个Application.dataPath
public string SERVER_RES_URL;// = "file:///C:/Res/"; //服务器文件的路径
public string LOCAL_RES_PATH;// = Application.dataPath + "/Res/"; //游戏文件的本地路径
private Dictionary<string,string> LocalResVersion; //本地配置文件的内存变量
private Dictionary<string, string> ServerResVersion; //服务器配置文件的内存变量
private List<string> NeedDownFiles; //需要更新的文件列表
private bool NeedUpdateLocalVersionFile = false; //是否需要更新
private void Awake()
{
NeedDownFiles = new List<string>();
VERSION_FILE = "version.txt";
LOCAL_RES_URL = "file://" + Application.dataPath + "/AssetBundles/";
SERVER_RES_URL = @"file://C:/Users/Administrator/Desktop/";
LOCAL_RES_PATH = Application.dataPath + "/AssetBundles/";
}
void Start()
{
//初始化
LocalResVersion = new Dictionary<string, string>(); // 将本地文件中的包名+MD5放到这个内存变量中
ServerResVersion = new Dictionary<string, string>(); //将服务器文件中的包名+MD5放到这个内存变量中
//加载本地version配置
StartCoroutine(DownLoad(LOCAL_RES_PATH + VERSION_FILE, delegate(WWW localVersion)
{
//保存本地的version
ParseVersionFile(localVersion.text, LocalResVersion);
//加载服务端version配置
StartCoroutine(DownLoad(SERVER_RES_URL + VERSION_FILE, delegate(WWW serverVersion)
{
//保存服务端version
ParseVersionFile(serverVersion.text, ServerResVersion);
//计算出需要重新加载的资源
CompareVersion();
//加载需要更新的资源
DownLoadRes();
}));
}));
}
//依次加载需要更新的资源
private void DownLoadRes() //这是一个递归
{
if (NeedDownFiles.Count == 0) //如果没有需要更新的文件 或者说需要更新的文件已经全更新完了
{
UpdateLocalVersionFile(); //更新本地配置文件
return;
}
string file = NeedDownFiles[0]; //获取当前列表中第一个需要更新的文件
NeedDownFiles.RemoveAt(0); //移除当前列表中第一个需要更新的文件
StartCoroutine(this.DownLoad(SERVER_RES_URL + file, delegate(WWW w) //从服务器通过文件名下载刚刚删除的内存变量中第一个需要更新的文件
{
//将下载的资源替换本地就的资源
ReplaceLocalRes(file, w.bytes);
DownLoadRes();
}));
}
private void ReplaceLocalRes(string fileName, byte[] data) //通过文件名替换/添加新文件 通过字节的方式传输
{
string filePath = LOCAL_RES_PATH + fileName; //存储一下当前下载的文件名,在前边加上放到的路径
FileStream stream = new FileStream(filePath, FileMode.Create); //创建一个文件路径为上边的变量
stream.Write(data, 0, data.Length); //将数据(ab包)字节写入文件流中
stream.Flush(); //将文件流强行写入硬盘
stream.Close(); //关闭文件流
}
//显示资源 待改为UnityWebRequest
private IEnumerator Show()
{
UnityWebRequest request = UnityWebRequest.GetAssetBundle("file://" + Application.dataPath + "/AssetBundles/" + "cube.ab");
yield return request.SendWebRequest();
//AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request); //这两种获取ab包的方式都可以
AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
print(ab.name);
GameObject go = ab.LoadAsset<GameObject>("Cube");
Instantiate(go);
ab.Unload(false);
request.Dispose();
}
//更新本地的version配置
private void UpdateLocalVersionFile()
{
if (NeedUpdateLocalVersionFile) //如果有需要更新的文件情况下执行(因为没有更新的文件情况下也会调用这个方法,下边有个Show方法(- -...)), 不能通过NeedDownFiles = 0来判断, 因为程序走到这里NeedDownFiles是一定等于0的,上边逐个更新逐个删除,所以为0
{
StringBuilder versions = new StringBuilder(); //创建字符流节省运算量,
foreach (var item in ServerResVersion) //遍历服务器文件的内存变量
{
versions.Append(item.Key).Append(",").Append(item.Value).Append("
"); //将里边的key+value遍历写入到字符流中
}
FileStream stream = new FileStream(LOCAL_RES_PATH + VERSION_FILE, FileMode.Create); //再创建个文件流
byte[] data = Encoding.UTF8.GetBytes(versions.ToString()); //把字符流转换为byte[] 因为写入时需要byte[]类型
stream.Write(data, 0, data.Length); //写入字符流内存
stream.Flush(); //强制写入硬盘
stream.Close(); //关闭字符流
}
//加载显示对象
StartCoroutine(Show());
}
private void CompareVersion()
{
foreach (var version in ServerResVersion) //遍历服务器变量
{
string fileName = version.Key; //保存其中一项Key
string serverMd5 = version.Value; //保存其中一项Value
//新增的资源
if (!LocalResVersion.ContainsKey(fileName)) //如果本地存储的内存文件中不包含服务器中这个项的Key(新资源)
{
NeedDownFiles.Add(fileName); //将这项添加到需要更新的List
}
else //否则可能是需要替换的资源也可能是不需要替换的,这需要通过MD5值进行判断
{
//需要替换的资源
string localMd5;
LocalResVersion.TryGetValue(fileName, out localMd5); //从本地文件判断一下服务器中这个文件的名字的MD5 并返回,以供下边判断同一个文件名的包是否是同一个MD5
if (!serverMd5.Equals(localMd5)) //当前服务器项中这个MD5不等于本地的,也就是不是一个资源, 那么需要更新
{
NeedDownFiles.Add(fileName); //添加到集合
}
}
}
//本次有更新,同时更新本地的version.txt
NeedUpdateLocalVersionFile = NeedDownFiles.Count > 0; //判断需要更新和添加的资源是否大于0 赋值给bool判断变量
}
private void ParseVersionFile(string content, Dictionary<string, string> dict) // 从www加载的text 保存到哪个字典中
{
if (content == null || content.Length == 0) //如果参数为null或者长度为null 不处理(正常不会走到这一步,一般是没有这个文件或者文件内容为空)
{
return;
}
string[] items = content.Split(new char[] { '
' }); //将文件中数据按回车分割到items数组中,也就是 文件名+MD5为一个数据
foreach (string item in items) //遍历
{
string[] info = item.Split(new char[] { ',' }); //将文件名和MD5分割
if (info != null && info.Length == 2) //安全校验
{
dict.Add(info[0], info[1]); //添加到传递过来的字典中 参数是本地文件内存变量就保存到本地内存变量,参数是服务器文件内存变量就保存到服务器文件内存变量
}
}
}
private IEnumerator DownLoad(string url, HandleFinishDownload finishFun) //带有委托的方法,让注册进委托的方法去调用(也就是上边的匿名函数)
{
WWW www = new WWW(url); //创建一个www实例, 待改为UnityWebRequest
yield return www;
if (finishFun != null) //如果参数不为空,也就是有方法被注册进委托
{
finishFun(www); //激活委托调用上边注册进来的的匿名函数
}
www.Dispose(); //释放www
}
public delegate void HandleFinishDownload(WWW www); //使用一个委托,才能使用匿名函数使代码更简洁
}
//********************************改为UnityWebRequest方式实现
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.IO;
using UnityEngine.Networking;
public class ResUpdate : MonoBehaviour
{
public string VERSION_FILE;// = "version.txt"; //文件名
public string LOCAL_RES_URL;// = "file://" + Application.dataPath + "/Res/"; //本地文件的路径加file是为了使用WWW拿到绝对路径,这个例子其实可以只使用下边一个Application.dataPath
public string SERVER_RES_URL;// = "file:///C:/Res/"; //服务器文件的路径
public string LOCAL_RES_PATH;// = Application.dataPath + "/Res/"; //游戏文件的本地路径
private Dictionary<string,string> LocalResVersion; //本地配置文件的内存变量
private Dictionary<string, string> ServerResVersion; //服务器配置文件的内存变量
private List<string> NeedDownFiles; //需要更新的文件列表
private bool NeedUpdateLocalVersionFile = false; //是否需要更新
private void Awake()
{
NeedDownFiles = new List<string>();
VERSION_FILE = "version.txt";
LOCAL_RES_URL = "file://" + Application.dataPath + "/AssetBundles/";
SERVER_RES_URL = @"file://C:/Users/Administrator/Desktop/";
LOCAL_RES_PATH = Application.dataPath + "/AssetBundles/";
}
void Start()
{
//初始化
LocalResVersion = new Dictionary<string, string>(); // 将本地文件中的包名+MD5放到这个内存变量中
ServerResVersion = new Dictionary<string, string>(); //将服务器文件中的包名+MD5放到这个内存变量中
//加载本地version配置
StartCoroutine(DownLoad(LOCAL_RES_PATH + VERSION_FILE, delegate(UnityWebRequest localVersion)
{
//保存本地的version
ParseVersionFile(localVersion.downloadHandler.text, LocalResVersion);
//加载服务端version配置
StartCoroutine(DownLoad(SERVER_RES_URL + VERSION_FILE, delegate(UnityWebRequest serverVersion)
{
//保存服务端version
ParseVersionFile(serverVersion.downloadHandler.text, ServerResVersion);
//计算出需要重新加载的资源
CompareVersion();
//加载需要更新的资源
DownLoadRes();
}));
}));
}
//依次加载需要更新的资源
private void DownLoadRes() //这是一个递归
{
if (NeedDownFiles.Count == 0) //如果没有需要更新的文件 或者说需要更新的文件已经全更新完了
{
UpdateLocalVersionFile(); //更新本地配置文件
return;
}
string file = NeedDownFiles[0]; //获取当前列表中第一个需要更新的文件
NeedDownFiles.RemoveAt(0); //移除当前列表中第一个需要更新的文件
StartCoroutine(this.DownLoad(SERVER_RES_URL + file, delegate(UnityWebRequest w) //从服务器通过文件名下载刚刚删除的内存变量中第一个需要更新的文件
{
//将下载的资源替换本地就的资源
ReplaceLocalRes(file, w.downloadHandler.data);
DownLoadRes();
}));
}
private void ReplaceLocalRes(string fileName, byte[] data) //通过文件名替换/添加新文件 通过字节的方式传输
{
string filePath = LOCAL_RES_PATH + fileName; //存储一下当前下载的文件名,在前边加上放到的路径
FileStream stream = new FileStream(filePath, FileMode.Create); //创建一个文件路径为上边的变量
stream.Write(data, 0, data.Length); //将数据(ab包)字节写入文件流中
stream.Flush(); //将文件流强行写入硬盘
stream.Close(); //关闭文件流
}
//显示资源 待改为UnityWebRequest
private IEnumerator Show()
{
UnityWebRequest request = UnityWebRequest.GetAssetBundle("file://" + Application.dataPath + "/AssetBundles/" + "cube.ab");
yield return request.SendWebRequest();
//AssetBundle ab = DownloadHandlerAssetBundle.GetContent(request); //这两种获取ab包的方式都可以
AssetBundle ab = (request.downloadHandler as DownloadHandlerAssetBundle).assetBundle;
print(ab.name);
GameObject go = ab.LoadAsset<GameObject>("Cube");
Instantiate(go);
ab.Unload(false);
request.Dispose();
}
//更新本地的version配置
private void UpdateLocalVersionFile()
{
if (NeedUpdateLocalVersionFile) //如果有需要更新的文件情况下执行(因为没有更新的文件情况下也会调用这个方法,下边有个Show方法(- -...)), 不能通过NeedDownFiles = 0来判断, 因为程序走到这里NeedDownFiles是一定等于0的,上边逐个更新逐个删除,所以为0
{
StringBuilder versions = new StringBuilder(); //创建字符流节省运算量,
foreach (var item in ServerResVersion) //遍历服务器文件的内存变量
{
versions.Append(item.Key).Append(",").Append(item.Value).Append("
"); //将里边的key+value遍历写入到字符流中
}
FileStream stream = new FileStream(LOCAL_RES_PATH + VERSION_FILE, FileMode.Create); //再创建个文件流
byte[] data = Encoding.UTF8.GetBytes(versions.ToString()); //把字符流转换为byte[] 因为写入时需要byte[]类型
stream.Write(data, 0, data.Length); //写入字符流内存
stream.Flush(); //强制写入硬盘
stream.Close(); //关闭字符流
}
//加载显示对象
StartCoroutine(Show());
}
private void CompareVersion()
{
foreach (var version in ServerResVersion) //遍历服务器变量
{
string fileName = version.Key; //保存其中一项Key
string serverMd5 = version.Value; //保存其中一项Value
//新增的资源
if (!LocalResVersion.ContainsKey(fileName)) //如果本地存储的内存文件中不包含服务器中这个项的Key(新资源)
{
NeedDownFiles.Add(fileName); //将这项添加到需要更新的List
}
else //否则可能是需要替换的资源也可能是不需要替换的,这需要通过MD5值进行判断
{
//需要替换的资源
string localMd5;
LocalResVersion.TryGetValue(fileName, out localMd5); //从本地文件判断一下服务器中这个文件的名字的MD5 并返回,以供下边判断同一个文件名的包是否是同一个MD5
if (!serverMd5.Equals(localMd5)) //当前服务器项中这个MD5不等于本地的,也就是不是一个资源, 那么需要更新
{
NeedDownFiles.Add(fileName); //添加到集合
}
}
}
//本次有更新,同时更新本地的version.txt
NeedUpdateLocalVersionFile = NeedDownFiles.Count > 0; //判断需要更新和添加的资源是否大于0 赋值给bool判断变量
}
private void ParseVersionFile(string content, Dictionary<string, string> dict) // 从www加载的text 保存到哪个字典中
{
if (content == null || content.Length == 0) //如果参数为null或者长度为null 不处理(正常不会走到这一步,一般是没有这个文件或者文件内容为空)
{
return;
}
string[] items = content.Split(new char[] { '
' }); //将文件中数据按回车分割到items数组中,也就是 文件名+MD5为一个数据
foreach (string item in items) //遍历
{
string[] info = item.Split(new char[] { ',' }); //将文件名和MD5分割
if (info != null && info.Length == 2) //安全校验
{
dict.Add(info[0], info[1]); //添加到传递过来的字典中 参数是本地文件内存变量就保存到本地内存变量,参数是服务器文件内存变量就保存到服务器文件内存变量
}
}
}
private IEnumerator DownLoad(string url, HandleFinishDownload finishFun) //带有委托的方法,让注册进委托的方法去调用(也就是上边的匿名函数)
{
UnityWebRequest request = UnityWebRequest.Get(url);
yield return request.SendWebRequest();
if (finishFun != null) //如果参数不为空,也就是有方法被注册进委托
{
finishFun(request); //激活委托调用上边注册进来的的匿名函数
}
request.Dispose(); //释放request
}
public delegate void HandleFinishDownload(UnityWebRequest request); //使用一个委托,才能使用匿名函数使代码更简洁
}