8.下载AB包

8.下载相关-下载AB包


8.1 知识点

下载文件函数修改成有返回布尔值

private bool DownLoadFile(string fileName, string localPath)
{
    try
    {
        // 下载文件逻辑...
        print(fileName + "下载成功");
        return true;
    }
    catch (Exception ex)
    {
        print(fileName + "下载失败" + ex.Message);
        return false;
    }
}

定义待下载的AB包列表和异步下载AB包函数,遍历远程AssetBundle信息字典添加AB包名到下载列表,循环异步下载

//这个是待下载的AB包列表文件 存储AB包的名字
private List<string> assetBundleDownLoadList = new List<string>();

/// <summary>
/// 下载远程AssetBundle文件的函数,异步执行。
/// </summary>
/// <param name="overCallBack">下载完成回调,参数为是否全部下载成功。</param>
/// <param name="updatePro">更新下载进度回调,参数为已下载资源数量和总资源数量。</param>
public async void DownLoadABFile(UnityAction<bool> overCallBack, UnityAction<int, int> updatePro)
{
    // 遍历字典的键,根据文件名将AssetBundle包添加到待下载列表中
    foreach (string name in remoteAssetBundleInfoDictionary.Keys)
    {
        // 直接将文件名放入待下载列表中
        assetBundleDownLoadList.Add(name);
    }

    // 本地存储路径,由于多线程不能访问Unity相关内容,因此在外部声明
    string localPath = Application.persistentDataPath + "/";

    // 是否下载成功的标志
    bool isOver = false;

    // 下载成功的文件名列表,用于移除下载成功的内容
    List<string> tempList = new List<string>();

    // 重新下载的最大次数
    int reDownLoadMaxNum = 5;

    // 下载成功的资源数量
    int downLoadOverNum = 0;

    // 需要下载的总资源数量
    int downLoadMaxNum = assetBundleDownLoadList.Count;

    // while循环的目的是进行n次重新下载,避免网络异常时下载失败
    while (assetBundleDownLoadList.Count > 0 && reDownLoadMaxNum > 0)
    {
        for (int i = 0; i < assetBundleDownLoadList.Count; i++)
        {
            // 通过Task.Run在异步线程中执行下载操作
            isOver = false;
            await Task.Run(() => {
                isOver = DownLoadFile(assetBundleDownLoadList[i], localPath + assetBundleDownLoadList[i]);
            });

            if (isOver)
            {
                // 更新下载进度,通知外部已下载资源数量和总资源数量
                updatePro(++downLoadOverNum, downLoadMaxNum);
                tempList.Add(assetBundleDownLoadList[i]); // 下载成功记录下来
            }
        }

        // 将下载成功的文件名从待下载列表中移除
        for (int i = 0; i < tempList.Count; i++)
            assetBundleDownLoadList.Remove(tempList[i]);

        // 递减重新下载的次数
        --reDownLoadMaxNum;
    }

    // 所有内容都下载完毕,通过回调告知外部是否下载完成
    overCallBack(assetBundleDownLoadList.Count == 0);
}

8.2 知识点代码

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 static ABUpdateMgr;

//AB包更新管理器
public class AssetBundleUpdateManager : BaseSingletonInMonoBehaviour<AssetBundleUpdateManager>
{
    //用于存储远端AB包信息的字典 之后 和本地进行对比即可完成 更新 下载相关逻辑
    private Dictionary<string, AssetBundleInfo> remoteAssetBundleInfoDictionary = new Dictionary<string, AssetBundleInfo>();

    //这个是待下载的AB包列表文件 存储AB包的名字
    private List<string> assetBundleDownLoadList = new List<string>();

    public void DownLoadABCompareFile()
    {
        //1.从资源服务器下载资源对比文件
        // www UnityWebRequest ftp相关api
        print(Application.persistentDataPath);
        DownLoadFile("ABCompareInfo.txt", Application.persistentDataPath + "/ABCompareInfo.txt");

        //2.就是获取资源对比文件中的 字符串信息 进行拆分
        string info = File.ReadAllText(Application.persistentDataPath + "/ABCompareInfo.txt");
        string[] strs = info.Split('|');//通过|拆分字符串 把一个个AB包信息拆分出来
        string[] infos = null;
        for (int i = 0; i < strs.Length; i++)
        {
            infos = strs[i].Split(' ');//又把一个AB的详细信息拆分出来
            //记录每一个远端AB包的信息 之后 好用来对比
            remoteAssetBundleInfoDictionary.Add(infos[0], new AssetBundleInfo(infos[0], infos[1], infos[2]));
        }

        print("远端AB包对比文件 加载结束");
    }

    private bool DownLoadFile(string fileName, string localPath)
    {
        try
        {
            //1.创建一个FTP连接 用于下载
            FtpWebRequest ftpWebRequest = FtpWebRequest.Create(new Uri("ftp://127.0.0.1/AB/PC/" + fileName)) as FtpWebRequest;

            //2.设置一个通信凭证 这样才能下载(如果有匿名账号 可以不设置凭证 但是实际开发中 建议 还是不要设置匿名账号)
            NetworkCredential networkCredential = new NetworkCredential("MrTao", "MrTao");
            ftpWebRequest.Credentials = networkCredential;

            //3.其它设置
            //  设置代理为null
            ftpWebRequest.Proxy = null;
            //  请求完毕后 是否关闭控制连接
            ftpWebRequest.KeepAlive = false;
            //  操作命令-下载
            ftpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile;
            //  指定传输的类型 2进制
            ftpWebRequest.UseBinary = true;

            //4.下载文件
            //  ftp的流对象
            FtpWebResponse ftpWebResponse = ftpWebRequest.GetResponse() as FtpWebResponse;
            Stream downLoadStream = ftpWebResponse.GetResponseStream();
            using (FileStream fileStream = File.Create(localPath))
            {
                //一点一点的下载内容
                byte[] bytes = new byte[2048];

                //返回值 代表读取了多少个字节
                int contentLength = downLoadStream.Read(bytes, 0, bytes.Length);

                //循环下载数据
                while (contentLength != 0)
                {
                    //写入到本地文件流中
                    fileStream.Write(bytes, 0, contentLength);
                    //写完再读
                    contentLength = downLoadStream.Read(bytes, 0, bytes.Length);
                }

                //循环完毕后 证明下载结束
                fileStream.Close();
                downLoadStream.Close();

                print(fileName + "下载成功");
                return true;
            }
        }
        catch (Exception ex)
        {
            print(fileName + "下载失败" + ex.Message);
            return false;
        }

    }

    /// <summary>
    /// 下载远程AssetBundle文件的函数,异步执行。
    /// </summary>
    /// <param name="overCallBack">下载完成回调,参数为是否全部下载成功。</param>
    /// <param name="updatePro">更新下载进度回调,参数为已下载资源数量和总资源数量。</param>
    public async void DownLoadABFile(UnityAction<bool> overCallBack, UnityAction<int, int> updatePro)
    {
        // 1. 遍历字典的键,根据文件名将AssetBundle包添加到待下载列表中
        foreach (string name in remoteAssetBundleInfoDictionary.Keys)
        {
            // 直接将文件名放入待下载列表中
            assetBundleDownLoadList.Add(name);
        }

        // 本地存储路径,由于多线程不能访问Unity相关内容,因此在外部声明
        string localPath = Application.persistentDataPath + "/";

        // 是否下载成功的标志
        bool isOver = false;

        // 下载成功的文件名列表,用于移除下载成功的内容
        List<string> tempList = new List<string>();

        // 重新下载的最大次数
        int reDownLoadMaxNum = 5;

        // 下载成功的资源数量
        int downLoadOverNum = 0;

        // 需要下载的总资源数量
        int downLoadMaxNum = assetBundleDownLoadList.Count;

        // while循环的目的是进行n次重新下载,避免网络异常时下载失败
        while (assetBundleDownLoadList.Count > 0 && reDownLoadMaxNum > 0)
        {
            for (int i = 0; i < assetBundleDownLoadList.Count; i++)
            {
                // 通过Task.Run在异步线程中执行下载操作
                isOver = false;
                await Task.Run(() => {
                    isOver = DownLoadFile(assetBundleDownLoadList[i], localPath + assetBundleDownLoadList[i]);
                });

                if (isOver)
                {
                    // 2. 更新下载进度,通知外部已下载资源数量和总资源数量
                    updatePro(++downLoadOverNum, downLoadMaxNum);
                    tempList.Add(assetBundleDownLoadList[i]); // 下载成功记录下来
                }
            }

            // 将下载成功的文件名从待下载列表中移除
            for (int i = 0; i < tempList.Count; i++)
                assetBundleDownLoadList.Remove(tempList[i]);

            // 递减重新下载的次数
            --reDownLoadMaxNum;
        }

        // 所有内容都下载完毕,通过回调告知外部是否下载完成
        overCallBack(assetBundleDownLoadList.Count == 0);
    }

}

8.3 练习题

在下载资源对比文件时,改成异步并加入判断成功与否,如果失败进行5次重新下载,把成功和失败的处理逻辑留给外部

修改下载资源对比文件函数为异步函数,定义一些变量,异步循环下载并最后回调后外部处理

// 异步下载资源对比文件的函数,提供回调通知下载完成状态
public async void DownLoadABCompareFile(UnityAction<bool> overCallBack)
{
    // 打印本地持久化数据路径
    print(Application.persistentDataPath);

    // 初始化下载是否成功的标志
    bool isOver = false;

    // 设置重新下载的最大次数
    int reDownLoadMaxNum = 5;

    // 本地存储路径,由于多线程不能访问Unity主线程的Application,所以在外面声明
    string localPath = Application.persistentDataPath;

    // 循环下载,直到下载成功或达到最大重新下载次数
    while (!isOver && reDownLoadMaxNum > 0)
    {
        // 使用异步任务Task.Run在后台线程中执行下载操作
        await Task.Run(() =>
        {
            // 执行下载文件的操作,将下载结果存储在isOver中
            isOver = DownLoadFile("ABCompareInfo.txt", localPath + "/ABCompareInfo.txt");
        });

        // 递减重新下载的次数
        --reDownLoadMaxNum;
    }

    // 通过回调通知外部下载是否完成
    overCallBack?.Invoke(isOver);
}

把获取下载好的AB包对比文件信息并加入到远程字典的逻辑抽离出函数

// 获取下载下来的AB包中的信息函数
public void GetRemoteABCompareFileInfo()
{
    // 读取资源对比文件的内容
    string info = File.ReadAllText(Application.persistentDataPath + "/ABCompareInfo.txt");

    // 使用竖线分割字符串,将每个AB包的信息分离
    string[] strs = info.Split('|');
    string[] infos = null;

    // 遍历每个分割后的AB包信息
    for (int i = 0; i < strs.Length; i++)
    {
        // 使用空格分割每个AB包的详细信息
        infos = strs[i].Split(' ');

        // 记录每一个远端AB包的信息,之后用于对比
        remoteAssetBundleInfoDictionary.Add(infos[0], new AssetBundleInfo(infos[0], infos[1], infos[2]));
    }

    // 打印信息,表示远端AB包对比文件内容获取结束
    print("远端AB包对比文件 内容获取结束");
}

注意调用时要确定文件下载成功才下一步

AssetBundleUpdateManager.Instance.DownLoadABCompareFile((isOver) => {
    //我们把解析下载下来的AB包对比文件写成了一个方法
    //一定是下载成功过后 再去解析它
    if (isOver)
    {
        //解析AB包对比文件
        AssetBundleUpdateManager.Instance.GetRemoteABCompareFileInfo();
        //下载AB包
        AssetBundleUpdateManager.Instance.DownLoadABFile((isOver) => {
            if (isOver)
            {
                print("所有AB包下载结束 ,继续处理逻辑");
            }
            else
            {
                print("下载失败,网络应该出现问题了,自己处理");
            }
        }, (nowNum, maxNum) => {
            print("下载进度" + nowNum + "/" + maxNum);
        });
    }
    //下载失败 就去处理失败的逻辑
    else
    {
        print("下载失败,网络应该出现问题了,自己处理");
    }
});

8.4 练习题代码

AssetBundleUpdateManager

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 static ABUpdateMgr;

//AB包更新管理器
public class AssetBundleUpdateManager : BaseSingletonInMonoBehaviour<AssetBundleUpdateManager>
{
    //用于存储远端AB包信息的字典 之后 和本地进行对比即可完成 更新 下载相关逻辑
    private Dictionary<string, AssetBundleInfo> remoteAssetBundleInfoDictionary = new Dictionary<string, AssetBundleInfo>();

    //这个是待下载的AB包列表文件 存储AB包的名字
    private List<string> assetBundleDownLoadList = new List<string>();

    private bool DownLoadFile(string fileName, string localPath)
    {
        try
        {
            //1.创建一个FTP连接 用于下载
            FtpWebRequest ftpWebRequest = FtpWebRequest.Create(new Uri("ftp://127.0.0.1/AB/PC/" + fileName)) as FtpWebRequest;

            //2.设置一个通信凭证 这样才能下载(如果有匿名账号 可以不设置凭证 但是实际开发中 建议 还是不要设置匿名账号)
            NetworkCredential networkCredential = new NetworkCredential("MrTao", "MrTao");
            ftpWebRequest.Credentials = networkCredential;

            //3.其它设置
            //  设置代理为null
            ftpWebRequest.Proxy = null;
            //  请求完毕后 是否关闭控制连接
            ftpWebRequest.KeepAlive = false;
            //  操作命令-下载
            ftpWebRequest.Method = WebRequestMethods.Ftp.DownloadFile;
            //  指定传输的类型 2进制
            ftpWebRequest.UseBinary = true;

            //4.下载文件
            //  ftp的流对象
            FtpWebResponse ftpWebResponse = ftpWebRequest.GetResponse() as FtpWebResponse;
            Stream downLoadStream = ftpWebResponse.GetResponseStream();
            using (FileStream fileStream = File.Create(localPath))
            {
                //一点一点的下载内容
                byte[] bytes = new byte[2048];

                //返回值 代表读取了多少个字节
                int contentLength = downLoadStream.Read(bytes, 0, bytes.Length);

                //循环下载数据
                while (contentLength != 0)
                {
                    //写入到本地文件流中
                    fileStream.Write(bytes, 0, contentLength);
                    //写完再读
                    contentLength = downLoadStream.Read(bytes, 0, bytes.Length);
                }

                //循环完毕后 证明下载结束
                fileStream.Close();
                downLoadStream.Close();

                print(fileName + "下载成功");
                return true;
            }
        }
        catch (Exception ex)
        {
            print(fileName + "下载失败" + ex.Message);
            return false;
        }

    }

    // 异步下载资源对比文件的函数,提供回调通知下载完成状态
    public async void DownLoadABCompareFile(UnityAction<bool> overCallBack)
    {
        // 打印本地持久化数据路径
        print(Application.persistentDataPath);

        // 初始化下载是否成功的标志
        bool isOver = false;

        // 设置重新下载的最大次数
        int reDownLoadMaxNum = 5;

        // 本地存储路径,由于多线程不能访问Unity主线程的Application,所以在外面声明
        string localPath = Application.persistentDataPath;

        // 循环下载,直到下载成功或达到最大重新下载次数
        while (!isOver && reDownLoadMaxNum > 0)
        {
            // 使用异步任务Task.Run在后台线程中执行下载操作
            await Task.Run(() =>
            {
                // 执行下载文件的操作,将下载结果存储在isOver中
                isOver = DownLoadFile("ABCompareInfo.txt", localPath + "/ABCompareInfo.txt");
            });

            // 递减重新下载的次数
            --reDownLoadMaxNum;
        }

        // 通过回调通知外部下载是否完成
        overCallBack?.Invoke(isOver);
    }

    // 获取下载下来的AB包中的信息函数
    public void GetRemoteABCompareFileInfo()
    {
        // 读取资源对比文件的内容
        string info = File.ReadAllText(Application.persistentDataPath + "/ABCompareInfo.txt");

        // 使用竖线分割字符串,将每个AB包的信息分离
        string[] strs = info.Split('|');
        string[] infos = null;

        // 遍历每个分割后的AB包信息
        for (int i = 0; i < strs.Length; i++)
        {
            // 使用空格分割每个AB包的详细信息
            infos = strs[i].Split(' ');

            // 记录每一个远端AB包的信息,之后用于对比
            remoteAssetBundleInfoDictionary.Add(infos[0], new AssetBundleInfo(infos[0], infos[1], infos[2]));
        }

        // 打印信息,表示远端AB包对比文件内容获取结束
        print("远端AB包对比文件 内容获取结束");
    }


    /// <summary>
    /// 下载远程AssetBundle文件的函数,异步执行。
    /// </summary>
    /// <param name="overCallBack">下载完成回调,参数为是否全部下载成功。</param>
    /// <param name="updatePro">更新下载进度回调,参数为已下载资源数量和总资源数量。</param>
    public async void DownLoadABFile(UnityAction<bool> overCallBack, UnityAction<int, int> updatePro)
    {
        // 1. 遍历字典的键,根据文件名将AssetBundle包添加到待下载列表中
        foreach (string name in remoteAssetBundleInfoDictionary.Keys)
        {
            // 直接将文件名放入待下载列表中
            assetBundleDownLoadList.Add(name);
        }

        // 本地存储路径,由于多线程不能访问Unity相关内容,因此在外部声明
        string localPath = Application.persistentDataPath + "/";

        // 是否下载成功的标志
        bool isOver = false;

        // 下载成功的文件名列表,用于移除下载成功的内容
        List<string> tempList = new List<string>();

        // 重新下载的最大次数
        int reDownLoadMaxNum = 5;

        // 下载成功的资源数量
        int downLoadOverNum = 0;

        // 需要下载的总资源数量
        int downLoadMaxNum = assetBundleDownLoadList.Count;

        // while循环的目的是进行n次重新下载,避免网络异常时下载失败
        while (assetBundleDownLoadList.Count > 0 && reDownLoadMaxNum > 0)
        {
            for (int i = 0; i < assetBundleDownLoadList.Count; i++)
            {
                // 通过Task.Run在异步线程中执行下载操作
                isOver = false;
                await Task.Run(() =>
                {
                    isOver = DownLoadFile(assetBundleDownLoadList[i], localPath + assetBundleDownLoadList[i]);
                });

                if (isOver)
                {
                    // 2. 更新下载进度,通知外部已下载资源数量和总资源数量
                    updatePro(++downLoadOverNum, downLoadMaxNum);
                    tempList.Add(assetBundleDownLoadList[i]); // 下载成功记录下来
                }
            }

            // 将下载成功的文件名从待下载列表中移除
            for (int i = 0; i < tempList.Count; i++)
                assetBundleDownLoadList.Remove(tempList[i]);

            // 递减重新下载的次数
            --reDownLoadMaxNum;
        }

        // 所有内容都下载完毕,通过回调告知外部是否下载完成
        overCallBack(assetBundleDownLoadList.Count == 0);
    }

}

Lesson08_练习题

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson08_练习题 : MonoBehaviour
{
    void Start()
    {
        AssetBundleUpdateManager.Instance.DownLoadABCompareFile((isOver) => {
            //我们把解析下载下来的AB包对比文件写成了一个方法
            //一定是下载成功过后 再去解析它
            if (isOver)
            {
                //解析AB包对比文件
                AssetBundleUpdateManager.Instance.GetRemoteABCompareFileInfo();
                //下载AB包
                AssetBundleUpdateManager.Instance.DownLoadABFile((isOver) => {
                    if (isOver)
                    {
                        print("所有AB包下载结束 ,继续处理逻辑");
                    }
                    else
                    {
                        print("下载失败,网络应该出现问题了,自己处理");
                    }
                }, (nowNum, maxNum) => {
                    print("下载进度" + nowNum + "/" + maxNum);
                });
            }
            //下载失败 就去处理失败的逻辑
            else
            {
                print("下载失败,网络应该出现问题了,自己处理");
            }
        });
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏