LSFE/LSFE.MobApp/Services/Coverage/FileDownloadService.cs
2026-07-30 10:31:04 +08:00

111 lines
4.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LSFE.MobApp.Services.Coverage
{
public interface IFileDownloadService
{
Task<string> DownloadFileAsync(string fileUrl, string fileName, CancellationToken cancellationToken);
Task<bool> IsFileDownloadedAsync(string fileName);
string GetLocalFilePath(string fileName);
}
public class FileDownloadService : IFileDownloadService
{
private readonly HttpClient _httpClient;
private readonly string _downloadFolder;
private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(3); // Limit concurrent downloads
public FileDownloadService(IHttpClientFactory httpClientFactory)
{
_httpClient = httpClientFactory.CreateClient("ApiClient");
_httpClient.Timeout = TimeSpan.FromMinutes(20);
_downloadFolder = Path.Combine(FileSystem.AppDataDirectory, "ProductMaterials");
Directory.CreateDirectory(_downloadFolder);
}
public async Task<string> DownloadFileAsync(string fileUrl, string fileName, CancellationToken cancellationToken = default)
{
await _semaphore.WaitAsync(cancellationToken);
try
{
var localPath = Path.Combine(_downloadFolder, fileName);
if (File.Exists(localPath))
{
var fileInfo = new FileInfo(localPath);
if (fileInfo.Length > 0)
return localPath;
File.Delete(localPath);
}
var tempPath = localPath + ".tmp";
using var response = await _httpClient.GetAsync(fileUrl, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
response.EnsureSuccessStatusCode();
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
await using var fileStream = new FileStream(tempPath, FileMode.Create, FileAccess.Write, FileShare.None);
await stream.CopyToAsync(fileStream, cancellationToken);
await fileStream.FlushAsync(cancellationToken);
fileStream.Close();
if (File.Exists(localPath))
File.Delete(localPath);
File.Move(tempPath, localPath);
return localPath;
}
catch (HttpRequestException ex) when (ex.InnerException is System.Security.Authentication.AuthenticationException)
{
Debug.WriteLine($"SSL/TLS error for {fileName}: {ex.Message}");
throw new InvalidOperationException("SSL certificate validation failed. Please check server certificate configuration.", ex);
}
catch (Exception ex)
{
Debug.WriteLine($"Download failed for {fileName}: {ex.Message}");
throw;
}
finally
{
_semaphore.Release();
}
}
public Task<bool> IsFileDownloadedAsync(string fileName)
{
try
{
if (string.IsNullOrWhiteSpace(fileName))
return Task.FromResult(false);
var localPath = Path.Combine(_downloadFolder, fileName);
if (!File.Exists(localPath))
return Task.FromResult(false);
// Verify file is not empty/corrupted
var fileInfo = new FileInfo(localPath);
return Task.FromResult(fileInfo.Length > 0);
}
catch (Exception ex)
{
Debug.WriteLine($"Error checking file existence for {fileName}: {ex.Message}");
return Task.FromResult(false);
}
}
public string GetLocalFilePath(string fileName)
{
return Path.Combine(_downloadFolder, fileName);
}
}
}