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的压缩和解压((可代密码)可一次压缩多个文件/文件夹)
四、效果预览
五、实现步骤
1、打开 Unity,新建空工程
2、导入 SharpZipLib 的 dll 支持包
具体SharpZipLib 下载使用可参见:Unity 工具 之 (SharpZipLib) 实现文件Zip的压缩和解压((可代密码)可一次压缩多个文件/文件夹)
3、在场景中,添加两个 RawImage,这里测试加载压缩包中的 某张图片
4、编写脚本逻辑,实现压缩文件的下载,和解压的指定功能,和测试功能的脚本
5、把测试脚本挂载到场景中,并对应赋值
6、这里同时测试 SharpZipLib 压缩文件功能,准备要压缩的文件夹和文件
7、测试脚本,先进行文件压缩,运行场景,即可在指定位置得到压缩的文件
8、修改测试脚本、开启测试网络下载压缩文件,并解压(包括两种方式解压),运行场景,效果如上
9、对应文件夹中下载的压缩包,和两种方式解压文件
六、关键代码
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;
}
}