Unity 工具类 之 WWW/UnityWebRequest 网络下载压缩文件(zip),解压到本地,且加载使用解压数据的简单案例(内也含压缩文件例子)

 

目录

Unity 工具类 之 WWW/UnityWebRequest 网络下载压缩文件(zip),解压到本地,且加载使用解压数据的简单案例(内也含压缩文件例子)

一、简单介绍

二、实现原理

三、注意事项

四、效果预览

五、实现步骤

六、关键代码


 

一、简单介绍

Unity 工具类,自己整理的一些游戏开发可能用到的模块,单独独立使用,方便游戏开发。

Unity 开发中,压缩文件/文件夹,然后网络从服务器下载压缩包到本地,接着本地解压压缩文件,并且使用其中的压缩包解压的文件的简单案例,方便自己日后类似的使用,如能帮到你,就更好了;如若有不对,还望指教。

 

二、实现原理

1、WWW/UnityWebRequest 获取压缩文件的 bytes 数据流;

2、使用 SharpZipLib 进行压缩文件的解压:

  • 可以直接解压 bytes 数据流
  • 也可以解压 bytes 保存的数据文件(也就是这里的下载的压缩包)

 

三、注意事项

1、由于使用 SharpZipLib 进行解压文件,最好使用是SharpZipLib 其压缩的文件;(同时案例中有压缩代码示例)

2、这里涉及到 SharpZipLib 的库使用

具体SharpZipLib 下载使用可参见:Unity 工具 之 (SharpZipLib) 实现文件Zip的压缩和解压((可代密码)可一次压缩多个文件/文件夹)

 

四、效果预览

unity 压缩率 unity 压缩文件_压缩包

 

五、实现步骤

1、打开 Unity,新建空工程

unity 压缩率 unity 压缩文件_下载解压压缩包_02

 

2、导入 SharpZipLib 的 dll  支持包

具体SharpZipLib 下载使用可参见:Unity 工具 之 (SharpZipLib) 实现文件Zip的压缩和解压((可代密码)可一次压缩多个文件/文件夹)

unity 压缩率 unity 压缩文件_unity 压缩率_03

 

3、在场景中,添加两个 RawImage,这里测试加载压缩包中的 某张图片

unity 压缩率 unity 压缩文件_unity 压缩率_04

 

4、编写脚本逻辑,实现压缩文件的下载,和解压的指定功能,和测试功能的脚本

unity 压缩率 unity 压缩文件_网络_05

 

5、把测试脚本挂载到场景中,并对应赋值

unity 压缩率 unity 压缩文件_压缩包_06

 

6、这里同时测试 SharpZipLib 压缩文件功能,准备要压缩的文件夹和文件

unity 压缩率 unity 压缩文件_下载解压压缩包_07

unity 压缩率 unity 压缩文件_压缩包_08

 

7、测试脚本,先进行文件压缩,运行场景,即可在指定位置得到压缩的文件

unity 压缩率 unity 压缩文件_网络_09

unity 压缩率 unity 压缩文件_unity 压缩率_10

 

8、修改测试脚本、开启测试网络下载压缩文件,并解压(包括两种方式解压),运行场景,效果如上

unity 压缩率 unity 压缩文件_Unity_11

 

9、对应文件夹中下载的压缩包,和两种方式解压文件

unity 压缩率 unity 压缩文件_unity 压缩率_12

unity 压缩率 unity 压缩文件_unity 压缩率_13

 

六、关键代码

1、Test_WWWDownloadZipAndUnzip

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;

public class Test_WWWDownloadZipAndUnzip : MonoBehaviour
{
    public RawImage rawImage;
    public RawImage rawImage1;

    string url;

    // Start is called before the first frame update
    void Start()
    {
        // 压缩文件
        //Test_Zip();

        // 下载解压文件
        url = Application.temporaryCachePath + "/tempZip.zip";
        wwwUnzip();
        wwwBytesUnzip();
    }


    #region zip 为后面测试网络下载zip和解压zip使用
    /// <summary>
    /// 打包zip 
    /// </summary>
    void Test_Zip()
    {
        // 要打包zip的路径数组
        string[] zipPaths = new string[] {
            @"D:\Tmp\temp\ZIPTest"
        };
        string zipOutputPath = Application.temporaryCachePath + "/tempZip.zip";
        Debug.Log("zipOutputPath : " + zipOutputPath);
        ZipWrapper.Zip(zipPaths, zipOutputPath);
    }
    #endregion

    #region Unzip

    void wwwUnzip()
    {

        string zipPath = Application.temporaryCachePath + "/wwwtempZip.zip";
        string exportPath = Application.temporaryCachePath + "/unzip";
        string imagePath = exportPath + "/ZIPTest/HipHop.jpg";
        WWWDownloadZipAndUnzip.Instance.WWWUnzip(url, zipPath, exportPath,()=> {
            var tex = new Texture2D(1, 1);

            var imageData = File.ReadAllBytes(imagePath);
            tex.LoadImage(imageData);

            rawImage.texture = tex;
        });
        
    }

    void wwwBytesUnzip()
    {
        string exportPath = Application.temporaryCachePath + "/unzipWithBytes";
        string imagePath = exportPath + "/ZIPTest/HipHop.jpg";
        WWWDownloadZipAndUnzip.Instance.WWWBytesUnzip(url, exportPath, ()=> {
            var tex = new Texture2D(1, 1);

            var imageData = File.ReadAllBytes(imagePath);
            tex.LoadImage(imageData);

            rawImage1.texture = tex;
        });
        
    }

    #endregion
}

 

2、WWWDownloadZipAndUnzip

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class WWWDownloadZipAndUnzip :MonoSingleton<WWWDownloadZipAndUnzip>
{

    Action wwwZipOnFinished;
    Action wwwZipBytesOnFinished;   

    #region www download and unzip

    /// <summary>
    /// 网络下载保存文件之后,再解压
    /// </summary>
    /// <param name="zipPath"></param>
    /// <param name="exportPath"></param>
    public void WWWUnzip(string url, string zipPath, string exportPath, Action onFinished) {

        wwwZipOnFinished = onFinished;
        StartCoroutine(LoadAndUnzip(url, zipPath, exportPath));

    }

    /// <summary>
    /// 协程网络下载zip,保存,并解压
    /// </summary>
    /// <param name="zipPath"></param>
    /// <param name="exportPath"></param>
    /// <returns></returns>
    IEnumerator LoadAndUnzip(string url, string zipPath, string exportPath)
    {
        
        Debug.Log("zipPath : " + zipPath);
        Debug.Log("exportPath : " + exportPath);

        WWW www = new WWW(url);

        yield return www;
        if (www.error != null)
        {
            Debug.Log("www.error : " + www.error);
        }
        else
        {
            var data = www.bytes;
            File.WriteAllBytes(zipPath, data);
            ZipWrapper.UnzipFile(zipPath, exportPath);

            if (wwwZipOnFinished != null)
            {
                wwwZipOnFinished();
            }

            // 酌情使用即可
            //File.Delete(zipPath);
            //Directory.Delete(exportPath, true);
        }

    }


    /// <summary>
    /// 网络下载bytes 直接解压成文件
    /// </summary>
    /// <param name="exportPath"></param>
    public void WWWBytesUnzip(string url, string exportPath, Action onFinished)
    {
        wwwZipBytesOnFinished = onFinished;
        //StartCoroutine(LoadAndBytesUnzip(url, exportPath));
        StartCoroutine(LoadAndBytesUnzipWithWeb(url, exportPath));
    }

    /// <summary>
    /// 协程网络下载zip,并解压
    /// </summary>
    /// <param name="url"></param>
    /// <param name="exportPath"></param>
    /// <returns></returns>
    IEnumerator LoadAndBytesUnzip(string url, string exportPath)
    {
      
        Debug.Log("exportPath : " + exportPath);

        WWW www = new WWW(url);

        yield return www;
        if (www.error != null)
        {
            Debug.Log("www.error : " + www.error);
        }
        else
        {
            var data = www.bytes;
            
            ZipWrapper.UnzipFile(data, exportPath);


            if (wwwZipBytesOnFinished != null)
            {
                wwwZipBytesOnFinished();
            }

            // 酌情使用即可
            //File.Delete(zipPath);
            //Directory.Delete(exportPath, true);
        }

    }

    /// <summary>
    /// 协程网络下载zip,并解压
    /// </summary>
    /// <param name="url"></param>
    /// <param name="exportPath"></param>
    /// <returns></returns>
    IEnumerator LoadAndBytesUnzipWithWeb(string url, string exportPath)
    {
       
        Debug.Log("exportPath : " + exportPath);

        using (UnityWebRequest uwr = UnityWebRequest.Get(url))
        {

            yield return uwr.SendWebRequest();
            if (uwr.isHttpError == true || uwr.isNetworkError == true)
            {
                Debug.Log("www.error : " + uwr.error);
            }
            else
            {
                var data = uwr.downloadHandler.data;

                ZipWrapper.UnzipFile(data, exportPath);

                if (wwwZipBytesOnFinished != null)
                {
                    wwwZipBytesOnFinished();
                }

                // 酌情使用即可
                //File.Delete(zipPath);
                //Directory.Delete(exportPath, true);
            }
        }

    }
    #endregion

}

 

3、ZipWrapper

using ICSharpCode.SharpZipLib.Zip;
using System.IO;
using UnityEngine;

public class ZipWrapper : MonoBehaviour
{
    #region ZipCallback
    public abstract class ZipCallback
    {
        /// <summary>
        /// 压缩单个文件或文件夹前执行的回调
        /// </summary>
        /// <param name="_entry"></param>
        /// <returns>如果返回true,则压缩文件或文件夹,反之则不压缩文件或文件夹</returns>
        public virtual bool OnPreZip(ZipEntry _entry)
        {
            return true;
        }

        /// <summary>
        /// 压缩单个文件或文件夹后执行的回调
        /// </summary>
        /// <param name="_entry"></param>
        public virtual void OnPostZip(ZipEntry _entry) { }

        /// <summary>
        /// 压缩执行完毕后的回调
        /// </summary>
        /// <param name="_result">true表示压缩成功,false表示压缩失败</param>
        public virtual void OnFinished(bool _result) { }
    }
    #endregion

    #region UnzipCallback
    public abstract class UnzipCallback
    {
        /// <summary>
        /// 解压单个文件或文件夹前执行的回调
        /// </summary>
        /// <param name="_entry"></param>
        /// <returns>如果返回true,则压缩文件或文件夹,反之则不压缩文件或文件夹</returns>
        public virtual bool OnPreUnzip(ZipEntry _entry)
        {
            return true;
        }

        /// <summary>
        /// 解压单个文件或文件夹后执行的回调
        /// </summary>
        /// <param name="_entry"></param>
        public virtual void OnPostUnzip(ZipEntry _entry) { }

        /// <summary>
        /// 解压执行完毕后的回调
        /// </summary>
        /// <param name="_result">true表示解压成功,false表示解压失败</param>
        public virtual void OnFinished(bool _result) { }
    }
    #endregion

    /// <summary>
    /// 压缩文件和文件夹
    /// </summary>
    /// <param name="_fileOrDirectoryArray">文件夹路径和文件名</param>
    /// <param name="_outputPathName">压缩后的输出路径文件名</param>
    /// <param name="_password">压缩密码</param>
    /// <param name="_zipCallback">ZipCallback对象,负责回调</param>
    /// <returns></returns>
    public static bool Zip(string[] _fileOrDirectoryArray, string _outputPathName, string _password = null, ZipCallback _zipCallback = null)
    {
        if ((null == _fileOrDirectoryArray) || string.IsNullOrEmpty(_outputPathName))
        {
            if (null != _zipCallback)
                _zipCallback.OnFinished(false);

            return false;
        }

        ZipOutputStream zipOutputStream = new ZipOutputStream(File.Create(_outputPathName));
        zipOutputStream.SetLevel(6);    // 压缩质量和压缩速度的平衡点
        if (!string.IsNullOrEmpty(_password))
            zipOutputStream.Password = _password;

        for (int index = 0; index < _fileOrDirectoryArray.Length; ++index)
        {
            bool result = false;
            string fileOrDirectory = _fileOrDirectoryArray[index];
            if (Directory.Exists(fileOrDirectory))
                result = ZipDirectory(fileOrDirectory, string.Empty, zipOutputStream, _zipCallback);
            else if (File.Exists(fileOrDirectory))
                result = ZipFile(fileOrDirectory, string.Empty, zipOutputStream, _zipCallback);

            if (!result)
            {
                if (null != _zipCallback)
                    _zipCallback.OnFinished(false);

                return false;
            }
        }

        zipOutputStream.Finish();
        zipOutputStream.Close();

        if (null != _zipCallback)
            _zipCallback.OnFinished(true);

        return true;
    }

    /// <summary>
    /// 解压Zip包
    /// </summary>
    /// <param name="_filePathName">Zip包的文件路径名</param>
    /// <param name="_outputPath">解压输出路径</param>
    /// <param name="_password">解压密码</param>
    /// <param name="_unzipCallback">UnzipCallback对象,负责回调</param>
    /// <returns></returns>
    public static bool UnzipFile(string _filePathName, string _outputPath, string _password = null, UnzipCallback _unzipCallback = null)
    {
        if (string.IsNullOrEmpty(_filePathName) || string.IsNullOrEmpty(_outputPath))
        {
            if (null != _unzipCallback)
                _unzipCallback.OnFinished(false);

            return false;
        }

        try
        {
            return UnzipFile(File.OpenRead(_filePathName), _outputPath, _password, _unzipCallback);
        }
        catch (System.Exception _e)
        {
            Debug.LogError("[ZipUtility.UnzipFile]: " + _e.ToString());

            if (null != _unzipCallback)
                _unzipCallback.OnFinished(false);

            return false;
        }
    }

    /// <summary>
    /// 解压Zip包
    /// </summary>
    /// <param name="_fileBytes">Zip包字节数组</param>
    /// <param name="_outputPath">解压输出路径</param>
    /// <param name="_password">解压密码</param>
    /// <param name="_unzipCallback">UnzipCallback对象,负责回调</param>
    /// <returns></returns>
    public static bool UnzipFile(byte[] _fileBytes, string _outputPath, string _password = null, UnzipCallback _unzipCallback = null)
    {
        if ((null == _fileBytes) || string.IsNullOrEmpty(_outputPath))
        {
            if (null != _unzipCallback)
                _unzipCallback.OnFinished(false);

            return false;
        }

        bool result = UnzipFile(new MemoryStream(_fileBytes), _outputPath, _password, _unzipCallback);
        if (!result)
        {
            if (null != _unzipCallback)
                _unzipCallback.OnFinished(false);
        }

        return result;
    }

    /// <summary>
    /// 解压Zip包
    /// </summary>
    /// <param name="_inputStream">Zip包输入流</param>
    /// <param name="_outputPath">解压输出路径</param>
    /// <param name="_password">解压密码</param>
    /// <param name="_unzipCallback">UnzipCallback对象,负责回调</param>
    /// <returns></returns>
    public static bool UnzipFile(Stream _inputStream, string _outputPath, string _password = null, UnzipCallback _unzipCallback = null)
    {
        if ((null == _inputStream) || string.IsNullOrEmpty(_outputPath))
        {
            if (null != _unzipCallback)
                _unzipCallback.OnFinished(false);

            return false;
        }

        // 创建文件目录
        if (!Directory.Exists(_outputPath))
            Directory.CreateDirectory(_outputPath);

        // 解压Zip包
        ZipEntry entry = null;
        using (ZipInputStream zipInputStream = new ZipInputStream(_inputStream))
        {
            if (!string.IsNullOrEmpty(_password))
                zipInputStream.Password = _password;

            while (null != (entry = zipInputStream.GetNextEntry()))
            {
                if (string.IsNullOrEmpty(entry.Name))
                    continue;

                if ((null != _unzipCallback) && !_unzipCallback.OnPreUnzip(entry))
                    continue;   // 过滤

                string filePathName = Path.Combine(_outputPath, entry.Name);

                // 创建文件目录
                if (entry.IsDirectory)
                {
                    Directory.CreateDirectory(filePathName);
                    continue;
                }

                // 写入文件
                try
                {
                    using (FileStream fileStream = File.Create(filePathName))
                    {
                        byte[] bytes = new byte[1024];
                        while (true)
                        {
                            int count = zipInputStream.Read(bytes, 0, bytes.Length);
                            if (count > 0)
                                fileStream.Write(bytes, 0, count);
                            else
                            {
                                if (null != _unzipCallback)
                                    _unzipCallback.OnPostUnzip(entry);

                                break;
                            }
                        }
                    }
                }
                catch (System.Exception _e)
                {
                    Debug.LogError("[ZipUtility.UnzipFile]: " + _e.ToString());

                    if (null != _unzipCallback)
                        _unzipCallback.OnFinished(false);

                    return false;
                }
            }
        }

        if (null != _unzipCallback)
            _unzipCallback.OnFinished(true);

        return true;
    }

    /// <summary>
    /// 压缩文件
    /// </summary>
    /// <param name="_filePathName">文件路径名</param>
    /// <param name="_parentRelPath">要压缩的文件的父相对文件夹</param>
    /// <param name="_zipOutputStream">压缩输出流</param>
    /// <param name="_zipCallback">ZipCallback对象,负责回调</param>
    /// <returns></returns>
    private static bool ZipFile(string _filePathName, string _parentRelPath, ZipOutputStream _zipOutputStream, ZipCallback _zipCallback = null)
    {

        //Crc32 crc32 = new Crc32();
        ZipEntry entry = null;
        FileStream fileStream = null;
        try
        {
            string entryName = _parentRelPath + '/' + Path.GetFileName(_filePathName);
            entry = new ZipEntry(entryName);
            entry.DateTime = System.DateTime.Now;

            if ((null != _zipCallback) && !_zipCallback.OnPreZip(entry))
                return true;    // 过滤

            fileStream = File.OpenRead(_filePathName);
            byte[] buffer = new byte[fileStream.Length];
            fileStream.Read(buffer, 0, buffer.Length);
            fileStream.Close();

            entry.Size = buffer.Length;

            //crc32.Reset();
            //crc32.Update(buffer);
            //entry.Crc = crc32.Value;

            _zipOutputStream.PutNextEntry(entry);
            _zipOutputStream.Write(buffer, 0, buffer.Length);
        }
        catch (System.Exception _e)
        {
            Debug.LogError("[ZipUtility.ZipFile]: " + _e.ToString());
            return false;
        }
        finally
        {
            if (null != fileStream)
            {
                fileStream.Close();
                fileStream.Dispose();
            }
        }

        if (null != _zipCallback)
            _zipCallback.OnPostZip(entry);

        return true;
    }

    /// <summary>
    /// 压缩文件夹
    /// </summary>
    /// <param name="_path">要压缩的文件夹</param>
    /// <param name="_parentRelPath">要压缩的文件夹的父相对文件夹</param>
    /// <param name="_zipOutputStream">压缩输出流</param>
    /// <param name="_zipCallback">ZipCallback对象,负责回调</param>
    /// <returns></returns>
    private static bool ZipDirectory(string _path, string _parentRelPath, ZipOutputStream _zipOutputStream, ZipCallback _zipCallback = null)
    {
        ZipEntry entry = null;
        try
        {
            string entryName = Path.Combine(_parentRelPath, Path.GetFileName(_path) + '/');
            entry = new ZipEntry(entryName);
            entry.DateTime = System.DateTime.Now;
            entry.Size = 0;

            if ((null != _zipCallback) && !_zipCallback.OnPreZip(entry))
                return true;    // 过滤

            _zipOutputStream.PutNextEntry(entry);
            _zipOutputStream.Flush();

            string[] files = Directory.GetFiles(_path);
            for (int index = 0; index < files.Length; ++index)
            {
                // 排除Unity中可能的 .meta 文件
                if (files[index].EndsWith(".meta")==true)
                {
                    Debug.LogWarning(files[index] + " not to zip");
                    continue;
                }

                ZipFile(files[index], Path.Combine(_parentRelPath, Path.GetFileName(_path)), _zipOutputStream, _zipCallback);
            }
        }
        catch (System.Exception _e)
        {
            Debug.LogError("[ZipUtility.ZipDirectory]: " + _e.ToString());
            return false;
        }

        string[] directories = Directory.GetDirectories(_path);
        for (int index = 0; index < directories.Length; ++index)
        {
            if (!ZipDirectory(directories[index], Path.Combine(_parentRelPath, Path.GetFileName(_path)), _zipOutputStream, _zipCallback))
            {
                return false;
            }
        }

        if (null != _zipCallback)
            _zipCallback.OnPostZip(entry);

        return true;
    }
}

 

4、MonoSingleton

using UnityEngine;

public abstract class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{
    private static T instance = null;

    private static readonly object locker = new object();

    private static bool bAppQuitting;

    public static T Instance
    {
        get
        {
            if (bAppQuitting)
            {
                instance = null;
                return instance;
            }

            lock (locker)
            {
                if (instance == null)
                {
                    instance = FindObjectOfType<T>();
                    if (FindObjectsOfType<T>().Length > 1)
                    {
                        Debug.LogError("不应该存在多个单例!");
                        return instance;
                    }

                    if (instance == null)
                    {
                        var singleton = new GameObject();
                        instance = singleton.AddComponent<T>();
                        singleton.name = "(singleton)" + typeof(T);
                        singleton.hideFlags = HideFlags.None;
                        DontDestroyOnLoad(singleton);
                    }
                    else
                        DontDestroyOnLoad(instance.gameObject);
                }
                instance.hideFlags = HideFlags.None;
                return instance;
            }
        }
    }

    private void Awake()
    {
        bAppQuitting = false;
    }

    private void OnDestroy()
    {
        bAppQuitting = true;
    }
}