140 lines
4.6 KiB
C#
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
|
|
}
|
|
}
|
|
}
|
|
}
|