111 lines
4.0 KiB
C#
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);
|
|
}
|
|
}
|
|
} |