兼容win7版应用
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

245 lines
8.7 KiB

using AIK.Common;
using AIK.Common.WebHelper;
using AIK.Models;
using AIK.Service.IService;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
namespace AIK.Service.Service
{
public class FileBinService : IFileCacheService
{
private readonly string _cacheBaseDirectory;
private readonly IHttpClientService _httpClient;
private readonly IConfigurationService _configurationService;
private readonly ILogger<FileBinService> _logger;
private string GetCacheFilePath(string cacheKey) => Path.Combine(_cacheBaseDirectory, $"{cacheKey}.bin");
public FileBinService(IConfigurationService configurationService, IHttpClientService httpClient, ILogger<FileBinService> logger)
{
_configurationService = configurationService;
_httpClient = httpClient;
_logger = logger;
// 设置缓存目录,例如在用户本地应用数据文件夹下
_cacheBaseDirectory = CommonString.BinCacheDirectory;
Directory.CreateDirectory(_cacheBaseDirectory); // 确保目录存在
}
public async Task ClearAllCacheAsync()
{
await Task.Run(() =>
{
try
{
var directory = new DirectoryInfo(_cacheBaseDirectory);
if (directory.Exists)
{
directory.Delete(true);
Directory.CreateDirectory(_cacheBaseDirectory);
_logger.LogInformation("清空所有缓存完成");
}
}
catch (Exception ex)
{
_logger.LogError(ex, "清空所有缓存失败");
}
});
}
public async Task ClearExpiredCacheAsync()
{
await Task.Run(() =>
{
try
{
var directory = new DirectoryInfo(_cacheBaseDirectory);
if (!directory.Exists) return;
int deletedCount = 0;
foreach (var file in directory.GetFiles("*.bin"))
{
try
{
file.Delete();
deletedCount++;
}
catch (Exception ex)
{
_logger.LogWarning(ex, "删除缓存文件失败: {FilePath}", file.FullName);
}
}
_logger.LogInformation("清理缓存完成,删除文件数: {DeletedCount}", deletedCount);
}
catch (Exception ex)
{
_logger.LogError(ex, "清理缓存失败");
}
});
}
public async Task<byte[]> GetFileByteAsync(string fileUrl, string cacheKey = null, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(fileUrl))
return GetEmptyByte();
// 生成缓存键,如果未提供则使用URL的MD5值
var key = cacheKey ?? GenerateCacheKey(fileUrl);
if (fileUrl.Contains("http"))
{
var urifileName = UrlHelper.GetEncodedFileNameFromUrl(fileUrl);
var realfileName = Uri.UnescapeDataString(urifileName);
key = GenerateCacheKey(realfileName);
}
// 2. 检查磁盘缓存
string filePath = GetCacheFilePath(key);
if (File.Exists(filePath))
{
//_logger.LogDebug("磁盘缓存命中: {FilePath}", filePath);
// 从磁盘加载并放入内存
var filebyte = await LoadFileFromDiskAsync(filePath);
return filebyte;
}
// 3. 从网络下载
try
{
var fullImgUrl = _configurationService.ApiSettings.FileUrl + fileUrl;
if (fileUrl.Contains("http"))
{
fullImgUrl = fileUrl;
}
_logger.LogInformation("开始下载bin文件: {Url}", fullImgUrl);
// Flurl.Http 字节下载(连接级显式 TLS 1.2;非 2xx 会抛异常,由外层 catch 统一处理)
byte[] fileBytes = await _httpClient.GetByteArrayWithTimeoutAsync(fullImgUrl);
if (fileBytes == null || fileBytes.Length == 0)
{
_logger.LogWarning("文件数据为空: {Url}", fullImgUrl);
return GetEmptyByte();
}
// 保存到磁盘缓存
await SavefileToDiskAsync(fileBytes, filePath);
return fileBytes;
}
catch (Exception ex)
{
_logger.LogError(ex, "下载文件失败: {fileUrl}", filePath);
}
return GetEmptyByte();
}
#region 辅助方法
private byte[] GetEmptyByte()
{
return Array.Empty<byte>();
}
/// <summary>
/// 保存字节数组到磁盘
/// </summary>
private async Task SavefileToDiskAsync(byte[] fileBytes, string filePath)
{
try
{
// 使用临时文件避免写入过程中出错
var tempFilePath = filePath + ".tmp";
File.WriteAllBytes(tempFilePath, fileBytes);
// 原子性操作:重命名临时文件
if (File.Exists(filePath))
{
File.Delete(filePath);
}
File.Move(tempFilePath, filePath);
_logger.LogDebug("文件保存成功: {FilePath}, 大小: {Size}字节", filePath, fileBytes.Length);
}
catch (Exception ex)
{
_logger.LogError(ex, "保存文件到磁盘失败: {FilePath}", filePath);
// 清理临时文件
var tempFilePath = filePath + ".tmp";
if (File.Exists(tempFilePath))
{
File.Delete(tempFilePath);
}
}
}
/// <summary>
/// 优化的磁盘加载方法
/// </summary>
private async Task<byte[]> LoadFileFromDiskAsync(string filePath)
{
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
{
return GetEmptyByte();
}
try
{
// 直接读取文件字节数组
byte[] fileBytes = File.ReadAllBytes(filePath);
if (fileBytes == null || fileBytes.Length == 0)
{
_logger.LogWarning("磁盘缓存文件为空: {FilePath}", filePath);
// 删除损坏的文件
File.Delete(filePath);
return GetEmptyByte();
}
return fileBytes;
}
catch (Exception ex)
{
_logger.LogError(ex, "从磁盘加载文件失败: {FilePath}", filePath);
return GetEmptyByte();
}
}
private string GenerateCacheKey(string url)
{
using (var md5 = System.Security.Cryptography.MD5.Create())
{
byte[] inputBytes = System.Text.Encoding.UTF8.GetBytes(url);
byte[] hashBytes = md5.ComputeHash(inputBytes);
return Convert.ToBase64String(hashBytes).Replace("/", "_").Replace("+", "-").Substring(0, 10);
}
}
#endregion
#region
private async Task UpdateOrAddBinFile(List<VehKeyClass> vehKeys)
{
var binFilePathList = new ConcurrentDictionary<string, string>();
foreach (var item in vehKeys)
{
var tempArray = item.sourceUrl.Split(',');
foreach (var tempitem in tempArray)
{
var filename = UrlHelper.GetEncodedFileNameFromUrl(tempitem);
binFilePathList.GetOrAdd(filename, tempitem);
}
}
}
public Task<(bool success, string localFilePath)> DownloadFileWithProgressAsync(string fileUrl, string cacheKey, IProgress<double> progress = null, CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
#endregion
}
}