using System;
using System.IO;
using System.Threading.Tasks;
namespace Nop.Plugin.Misc.FruitBankPlugin.Services.FileStorage
{
///
/// Local file system storage provider - saves files to wwwroot/uploads
///
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);
}
}
///
/// Saves a file stream to local file system
///
public async Task 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;
}
///
/// Retrieves a file stream from local file system
///
public async Task 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;
}
///
/// Deletes a file from local file system
///
public Task 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);
}
///
/// Checks if a file exists in local file system
///
public Task FileExistsAsync(string relativePath)
{
if (string.IsNullOrWhiteSpace(relativePath))
return Task.FromResult(false);
var fullPath = Path.Combine(_baseDirectory, relativePath);
return Task.FromResult(File.Exists(fullPath));
}
///
/// Cleans up empty parent directories after file deletion
///
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
}
}
}
}