Mango.Nop.Plugins/Nop.Plugin.Misc.AIPlugin/Services/LocalStorageProvider.cs

140 lines
4.6 KiB
C#

using System;
using System.IO;
using System.Threading.Tasks;
namespace Nop.Plugin.Misc.FruitBankPlugin.Services.FileStorage
{
/// <summary>
/// Local file system storage provider - saves files to wwwroot/uploads
/// </summary>
public class LocalFileStorageProvider : IFileStorageProvider
{
private readonly string _baseDirectory;
public LocalFileStorageProvider(string baseDirectory = null)
{
// Default to wwwroot/uploads if not specified
_baseDirectory = baseDirectory ?? Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads");
// Ensure base directory exists
if (!Directory.Exists(_baseDirectory))
{
Directory.CreateDirectory(_baseDirectory);
}
}
/// <summary>
/// Saves a file stream to local file system
/// </summary>
public async Task<string> SaveFileAsync(Stream fileStream, string relativePath)
{
if (fileStream == null)
throw new ArgumentNullException(nameof(fileStream));
if (string.IsNullOrWhiteSpace(relativePath))
throw new ArgumentNullException(nameof(relativePath));
var fullPath = Path.Combine(_baseDirectory, relativePath);
var directory = Path.GetDirectoryName(fullPath);
// Ensure directory exists
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// Save file
using (var fileStreamOutput = new FileStream(fullPath, FileMode.Create, FileAccess.Write, FileShare.None))
{
await fileStream.CopyToAsync(fileStreamOutput);
}
return fullPath;
}
/// <summary>
/// Retrieves a file stream from local file system
/// </summary>
public async Task<Stream> GetFileAsync(string relativePath)
{
if (string.IsNullOrWhiteSpace(relativePath))
throw new ArgumentNullException(nameof(relativePath));
var fullPath = Path.Combine(_baseDirectory, relativePath);
if (!File.Exists(fullPath))
throw new FileNotFoundException($"File not found: {relativePath}", fullPath);
// Read file into memory stream to avoid file locks
var memoryStream = new MemoryStream();
using (var fileStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
await fileStream.CopyToAsync(memoryStream);
}
memoryStream.Position = 0;
return memoryStream;
}
/// <summary>
/// Deletes a file from local file system
/// </summary>
public Task<bool> DeleteFileAsync(string relativePath)
{
if (string.IsNullOrWhiteSpace(relativePath))
throw new ArgumentNullException(nameof(relativePath));
var fullPath = Path.Combine(_baseDirectory, relativePath);
if (File.Exists(fullPath))
{
File.Delete(fullPath);
// Try to clean up empty directories
CleanupEmptyDirectories(fullPath);
return Task.FromResult(true);
}
return Task.FromResult(false);
}
/// <summary>
/// Checks if a file exists in local file system
/// </summary>
public Task<bool> FileExistsAsync(string relativePath)
{
if (string.IsNullOrWhiteSpace(relativePath))
return Task.FromResult(false);
var fullPath = Path.Combine(_baseDirectory, relativePath);
return Task.FromResult(File.Exists(fullPath));
}
/// <summary>
/// Cleans up empty parent directories after file deletion
/// </summary>
private void CleanupEmptyDirectories(string filePath)
{
try
{
var directory = Path.GetDirectoryName(filePath);
while (!string.IsNullOrEmpty(directory) &&
directory != _baseDirectory &&
Directory.Exists(directory) &&
Directory.GetFiles(directory).Length == 0 &&
Directory.GetDirectories(directory).Length == 0)
{
Directory.Delete(directory);
directory = Path.GetDirectoryName(directory);
}
}
catch
{
// Ignore cleanup errors - not critical
}
}
}
}