FM/Assets/Scripts/Base/Net/AliyunOSSManager.cs

423 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Aliyun.OSS;
using Aliyun.OSS.Common;
using UnityEngine;
namespace HK
{
[System.Serializable]
public class OSSConfig
{
// 阿里云AccessKeyId
public string accessKeyId = "LTAI5t9AcXJm2BXwzLTie8Wa";
// 阿里云AccessKeySecret
public string accessKeySecret = "RJhANJfNL7Xr8EmEZfMR12ED5nIp8c";
// OSS服务地址如"oss-cn-beijing.aliyuncs.com
public string endpoint = "oss-cn-hongkong.aliyuncs.com";
// 存储空间名称
public string bucketName = "kiosk-assets-bundle";
// OSS上的资源根存储目录
public string ossRootDirectory = "Framework"; //
// OSS上的资源相对存储目录
public string ossRelativePath = ""; //
}
/// <summary>
/// 阿里云OSS管理单例类
/// 负责OSS的初始化、文件上传和下载
/// </summary>
public class AliyunOSSManager : IDisposable
{
// 单例实例
private static readonly Lazy<AliyunOSSManager> _instance =
new Lazy<AliyunOSSManager>(() => new AliyunOSSManager());
public static AliyunOSSManager Instance => _instance.Value;
// OSS客户端
private OssClient _ossClient;
private string _bucketName;
private string _baseOssPath; // OSS基础路径如"GameRes/"
private bool _isInitialized = false;
// 私有构造函数防止外部实例化
private AliyunOSSManager()
{
}
/// <summary>
/// 初始化OSS客户端
/// </summary>
/// <param name="config">阿里云</param>
/// <returns>是否初始化成功</returns>
public bool Initialize(OSSConfig config)
{
if (_isInitialized)
{
Debug.LogWarning("OSS客户端已初始化无需重复初始化");
return true;
}
try
{
// 验证基础路径格式
if (!config.ossRootDirectory.EndsWith("/"))
config.ossRootDirectory += "/";
// 初始化客户端
_ossClient = new OssClient(config.endpoint, config.accessKeyId, config.accessKeySecret);
_bucketName = config.bucketName;
_baseOssPath = config.ossRootDirectory;
// 检查Bucket是否存在
if (!_ossClient.DoesBucketExist(config.bucketName))
{
Debug.LogWarning($"Bucket不存在正在创建: {config.bucketName}");
_ossClient.CreateBucket(config.bucketName);
}
_isInitialized = true;
Debug.Log("OSS客户端初始化成功");
return true;
}
catch (OssException ex)
{
Debug.LogError($"OSS初始化失败: {ex.Message} 错误代码: {ex.ErrorCode}");
return false;
}
catch (Exception ex)
{
Debug.LogError($"初始化异常: {ex.Message}");
return false;
}
}
/// <summary>
/// 上传单个文件到OSS
/// </summary>
/// <param name="localFilePath">本地文件路径</param>
/// <param name="ossRelativePath">OSS上的相对路径相对于baseOssPath</param>
/// <returns>是否上传成功</returns>
public async Task<bool> UploadFileAsync(string localFilePath, string ossRelativePath)
{
if (!CheckInitialized()) return false;
if (!File.Exists(localFilePath))
{
Debug.LogError($"本地文件不存在: {localFilePath}");
return false;
}
try
{
// 构建完整的OSS路径
string fullOssPath = GetFullOssPath(ossRelativePath);
// 使用异步上传
await Task.Run(() =>
{
using (var fileStream = File.OpenRead(localFilePath))
{
_ossClient.PutObject(_bucketName, fullOssPath, fileStream);
}
});
Debug.Log($"文件上传成功: {fullOssPath}");
return true;
}
catch (OssException ex)
{
Debug.LogError($"文件上传失败: {ex.Message} 错误代码: {ex.ErrorCode}");
return false;
}
catch (Exception ex)
{
Debug.LogError($"上传异常: {ex.Message}");
return false;
}
}
/// <summary>
/// 上传目录下所有文件到OSS
/// </summary>
/// <param name="localDirectory">本地目录</param>
/// <param name="ossDirectory">OSS上的目录相对于baseOssPath</param>
/// <param name="progressCallback">进度回调 (当前文件数, 总文件数)</param>
/// <returns>是否上传成功</returns>
public async Task<bool> UploadDirectoryAsync(string localDirectory, string ossDirectory,
Action<int, int> progressCallback = null)
{
if (!CheckInitialized()) return false;
if (!Directory.Exists(localDirectory))
{
Debug.LogError($"本地目录不存在: {localDirectory}");
return false;
}
try
{
// 获取所有文件
string[] allFiles = Directory.GetFiles(localDirectory, "*.*", SearchOption.AllDirectories);
int totalCount = allFiles.Length;
int currentCount = 0;
foreach (string filePath in allFiles)
{
// 计算相对路径
string relativePath = Path.GetRelativePath(localDirectory, filePath);
// 转换为OSS路径格式替换反斜杠为正斜杠
relativePath = relativePath.Replace('\\', '/');
// 构建OSS目标路径
string ossTargetPath = string.IsNullOrEmpty(ossDirectory)
? relativePath
: $"{ossDirectory}/{relativePath}";
// 上传单个文件
bool success = await UploadFileAsync(filePath, ossTargetPath);
if (!success)
{
Debug.LogError($"目录上传中断,文件上传失败: {filePath}");
return false;
}
// 进度回调
currentCount++;
progressCallback?.Invoke(currentCount, totalCount);
}
Debug.Log($"目录上传完成,共上传 {totalCount} 个文件");
return true;
}
catch (Exception ex)
{
Debug.LogError($"目录上传异常: {ex.Message}");
return false;
}
}
public async Task<bool> UploadDirectoryAsync(string localDirectory, string ossDirectory, CancellationToken cancellationToken = default,
Action<int, int> progressCallback = null)
{
if (!CheckInitialized()) return false;
if (!Directory.Exists(localDirectory))
{
Debug.LogError($"本地目录不存在: {localDirectory}");
return false;
}
try
{
// 检查是否已取消
cancellationToken.ThrowIfCancellationRequested();
// 获取所有文件
string[] allFiles = Directory.GetFiles(localDirectory, "*.*", SearchOption.AllDirectories);
int totalCount = allFiles.Length;
int currentCount = 0;
foreach (string filePath in allFiles)
{
// 每次循环开始前检查是否取消
cancellationToken.ThrowIfCancellationRequested();
// 计算相对路径
string relativePath = Path.GetRelativePath(localDirectory, filePath);
// 转换为OSS路径格式替换反斜杠为正斜杠
relativePath = relativePath.Replace('\\', '/');
// 构建OSS目标路径
string ossTargetPath = string.IsNullOrEmpty(ossDirectory)
? relativePath
: $"{ossDirectory}/{relativePath}";
// 上传单个文件(传入取消令牌)
bool success = await UploadFileAsync(filePath, ossTargetPath);
if (!success)
{
Debug.LogError($"目录上传中断,文件上传失败: {filePath}");
return false;
}
// 进度回调
currentCount++;
progressCallback?.Invoke(currentCount, totalCount);
}
Debug.Log($"目录上传完成,共上传 {totalCount} 个文件");
return true;
}
catch (OperationCanceledException)
{
Debug.Log("目录上传已被取消");
return false;
}
catch (Exception ex)
{
Debug.LogError($"目录上传异常: {ex.Message}");
return false;
}
}
/// <summary>
/// 从OSS下载文件到本地优化版
/// </summary>
/// <param name="ossRelativePath">OSS上的相对路径相对于baseOssPath</param>
/// <param name="localSavePath">本地保存路径</param>
/// <returns>是否下载成功</returns>
public async Task<bool> DownloadFileAsync(string ossRelativePath, string localSavePath)
{
if (!CheckInitialized()) return false;
// 临时文件路径(避免下载中断导致的文件损坏)
string tempSavePath = $"{localSavePath}.tmp";
// 构建完整的OSS路径
string fullOssPath = GetFullOssPath(ossRelativePath);
try
{
// 确保保存目录存在
string directory = Path.GetDirectoryName(localSavePath);
if (!Directory.Exists(directory))
Directory.CreateDirectory(directory);
// 异步下载并保存文件
bool downloadSuccess = await Task.Run(() =>
{
try
{
// 获取OSS对象
using (OssObject ossObject = _ossClient.GetObject(_bucketName, fullOssPath))
{
// 读取对象内容流并写入本地文件
using (Stream sourceStream = ossObject.Content)
{
using (FileStream targetStream = File.OpenWrite(tempSavePath))
{
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = sourceStream.Read(buffer, 0, buffer.Length)) > 0)
{
targetStream.Write(buffer, 0, bytesRead);
}
}
}
}
// 下载完成后重命名临时文件(原子操作,避免文件损坏)
if (File.Exists(localSavePath))
File.Delete(localSavePath);
File.Move(tempSavePath, localSavePath);
return true;
}
catch (Exception ex)
{
Debug.LogError($"下载任务执行失败: {ex.Message}");
return false;
}
});
if (downloadSuccess)
{
Debug.Log($"文件下载成功: {localSavePath}");
return true;
}
else
{
Debug.LogError($"文件下载失败: {fullOssPath}");
return false;
}
}
catch (OssException ex)
{
// 处理OSS特定错误
if (ex.ErrorCode == "NoSuchKey")
Debug.LogError($"OSS文件不存在: {fullOssPath}");
else
Debug.LogError($"OSS错误: {ex.Message} 错误代码: {ex.ErrorCode}");
}
catch (Exception ex)
{
Debug.LogError($"下载异常: {ex.Message}");
}
finally
{
// 清理临时文件
if (File.Exists(tempSavePath))
File.Delete(tempSavePath);
}
return false;
}
/// <summary>
/// 检查文件是否在OSS上存在
/// </summary>
/// <param name="ossRelativePath">OSS相对路径</param>
/// <returns>是否存在</returns>
public bool ExistsOnOss(string ossRelativePath)
{
if (!CheckInitialized()) return false;
try
{
string fullOssPath = GetFullOssPath(ossRelativePath);
_ossClient.GetObjectMetadata(_bucketName, fullOssPath);
return true;
}
catch (OssException ex)
{
if (ex.ErrorCode == "NoSuchKey")
return false;
Debug.LogError($"检查文件存在性失败: {ex.Message}");
return false;
}
}
/// <summary>
/// 构建完整的OSS路径
/// </summary>
private string GetFullOssPath(string relativePath)
{
// 移除可能的前导斜杠,避免重复
if (relativePath.StartsWith("/"))
relativePath = relativePath.Substring(1);
return $"{_baseOssPath}{relativePath}";
}
/// <summary>
/// 检查是否已初始化
/// </summary>
private bool CheckInitialized()
{
if (!_isInitialized)
{
Debug.LogError("OSS客户端未初始化请先调用Initialize方法");
return false;
}
return true;
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
// OSS客户端在.NET SDK中没有Dispose方法此处主要用于清理自定义资源
_isInitialized = false;
Debug.Log("OSS管理器已释放资源");
}
}
}