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
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
|
|
}
|
|
}
|
|
|