Skip to main content

Overview

The HelperPathProvider is a utility class that simplifies file path management in ASP.NET Core MVC applications. It provides methods to map physical file system paths and generate full URLs for files stored in predefined folders.

Key Features

  • Convert logical folder names to physical file paths
  • Generate full URLs for accessing files
  • Support for multiple predefined folder types
  • Server address detection for URL generation
  • Integration with ASP.NET Core hosting environment

HelperPathProvider Class

Namespace: MvcNetCoreUtilidades.Helpers Dependencies:
  • IWebHostEnvironment - Provides access to web root path
  • IServer - Provides server address information for URL generation

Constructor

public HelperPathProvider(IWebHostEnvironment hostEnvironment, IServer server)
{
    this.hostEnvironment = hostEnvironment;
    this.server = server;
}

Folders Enumeration

Defines the available folder types for file storage.
public enum Folders
{
    Images,      // Maps to 'images' folder
    facturas,    // Maps to 'facturas' folder
    Uploads,     // Maps to 'uploads' folder
    Temporal     // Maps to 'temp' folder
}

Folder Mapping

Enum ValuePhysical FolderDescription
Folders.Imagesimages/Image files
Folders.facturasfacturas/Invoice documents
Folders.Uploadsuploads/General file uploads
Folders.Temporaltemp/Temporary files

Methods

MapPath

Generates the full physical file system path for a file.
FileName
string
required
The name of the file (including extension)
folder
Folders
required
The folder enumeration value where the file should be stored
Returns: string - The complete physical path to the file
public string MapPath(string FileName, Folders folder)
{
    string carpeta = "";
    if (folder == Folders.Images)
    {
        carpeta = "images";
    }
    else if (folder == Folders.facturas)
    {
        carpeta = "facturas";
    }
    else if (folder == Folders.Uploads)
    {
        carpeta = "uploads";
    }
    else if (folder == Folders.Temporal)
    {
        carpeta = "temp";
    }

    string rootPath = this.hostEnvironment.WebRootPath;
    string path = Path.Combine(rootPath, carpeta, FileName);
    return path;
}
Example:
string path = helper.MapPath("profile.jpg", Folders.Images);
// Result: /var/www/myapp/wwwroot/images/profile.jpg

MapUrlPath

Generates the full URL for accessing a file through the web server.
fileName
string
required
The name of the file (including extension)
folder
Folders
required
The folder enumeration value where the file is stored
Returns: string - The complete URL to access the file
public string MapUrlPath(string fileName, Folders folder)
{
    string carpeta = "";
    if (folder == Folders.Images)
    {
        carpeta = "images";
    }
    else if (folder == Folders.facturas)
    {
        carpeta = "facturas";
    }
    else if (folder == Folders.Uploads)
    {
        carpeta = "uploads";
    }
    else if (folder == Folders.Temporal)
    {
        carpeta = "temp";
    }
    
    var adresses = this.server.Features.Get<IServerAddressesFeature>().Addresses;
    string serverUrl = adresses.FirstOrDefault();
    string urlPath = serverUrl + "/" + carpeta + "/" + fileName;
    return urlPath;
}
Example:
string url = helper.MapUrlPath("profile.jpg", Folders.Images);
// Result: https://localhost:5001/images/profile.jpg

Configuration

1

Register as singleton

Register HelperPathProvider in Program.cs:
using MvcNetCoreUtilidades.Helpers;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<HelperPathProvider>();

builder.Services.AddControllersWithViews();
Registered as singleton since it has no state and can be reused across requests.
2

Create folder structure

Ensure folders exist in your wwwroot directory:
wwwroot/
├── images/
├── facturas/
├── uploads/
└── temp/
3

Inject into controllers

Inject through constructor:
public class FilesController : Controller
{
    private readonly HelperPathProvider _pathHelper;

    public FilesController(HelperPathProvider pathHelper)
    {
        _pathHelper = pathHelper;
    }
}

Usage Examples

Example 1: File Upload with Path Mapping

using Microsoft.AspNetCore.Mvc;
using MvcNetCoreUtilidades.Helpers;

public class UploadController : Controller
{
    private readonly HelperPathProvider _helper;

    public UploadController(HelperPathProvider helper)
    {
        _helper = helper;
    }

    [HttpPost]
    public async Task<IActionResult> Upload(IFormFile file)
    {
        if (file == null || file.Length == 0)
            return BadRequest("No file uploaded");

        // Get physical path
        string physicalPath = _helper.MapPath(file.FileName, Folders.Uploads);
        
        // Save file
        using (var stream = new FileStream(physicalPath, FileMode.Create))
        {
            await file.CopyToAsync(stream);
        }

        // Get URL for displaying
        string fileUrl = _helper.MapUrlPath(file.FileName, Folders.Uploads);
        
        ViewData["FileUrl"] = fileUrl;
        ViewData["Message"] = "File uploaded successfully!";
        
        return View();
    }
}
public class GalleryController : Controller
{
    private readonly HelperPathProvider _helper;

    public GalleryController(HelperPathProvider helper)
    {
        _helper = helper;
    }

    public IActionResult Index()
    {
        // Get all image files from images folder
        string imagesPath = _helper.MapPath("", Folders.Images);
        var imageFiles = Directory.GetFiles(imagesPath)
            .Select(Path.GetFileName)
            .ToList();

        // Generate URLs for each image
        var imageUrls = imageFiles
            .Select(fileName => _helper.MapUrlPath(fileName, Folders.Images))
            .ToList();

        return View(imageUrls);
    }
}

Example 3: Invoice Download Service

public class InvoiceService
{
    private readonly HelperPathProvider _helper;

    public InvoiceService(HelperPathProvider helper)
    {
        _helper = helper;
    }

    public async Task<string> GenerateInvoiceAsync(Invoice invoice)
    {
        // Generate unique filename
        string fileName = $"invoice_{invoice.Id}_{DateTime.Now:yyyyMMdd}.pdf";
        
        // Get physical path
        string filePath = _helper.MapPath(fileName, Folders.facturas);
        
        // Generate PDF (pseudo-code)
        await GeneratePdfAsync(invoice, filePath);
        
        // Return URL for download
        return _helper.MapUrlPath(fileName, Folders.facturas);
    }

    public FileStream GetInvoiceStream(string fileName)
    {
        string filePath = _helper.MapPath(fileName, Folders.facturas);
        
        if (!File.Exists(filePath))
            throw new FileNotFoundException("Invoice not found");
        
        return new FileStream(filePath, FileMode.Open, FileAccess.Read);
    }
}

Example 4: Temporary File Cleanup

public class FileCleanupService : BackgroundService
{
    private readonly HelperPathProvider _helper;
    private readonly ILogger<FileCleanupService> _logger;

    public FileCleanupService(HelperPathProvider helper, ILogger<FileCleanupService> logger)
    {
        _helper = helper;
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                CleanupTemporaryFiles();
                await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Error during file cleanup");
            }
        }
    }

    private void CleanupTemporaryFiles()
    {
        // Get temp folder path
        string tempPath = _helper.MapPath("", Folders.Temporal);
        
        if (!Directory.Exists(tempPath))
            return;

        var cutoffTime = DateTime.Now.AddHours(-24);
        var oldFiles = Directory.GetFiles(tempPath)
            .Where(file => File.GetCreationTime(file) < cutoffTime);

        foreach (var file in oldFiles)
        {
            try
            {
                File.Delete(file);
                _logger.LogInformation($"Deleted temporary file: {Path.GetFileName(file)}");
            }
            catch (Exception ex)
            {
                _logger.LogWarning(ex, $"Failed to delete file: {file}");
            }
        }
    }
}

Advanced Patterns

Custom Folder Extension

Extend the enum to support custom folders:
// Create your own enum
public enum CustomFolders
{
    Documents,
    Videos,
    Archives
}

// Create extended helper
public class ExtendedPathProvider : HelperPathProvider
{
    public ExtendedPathProvider(IWebHostEnvironment hostEnvironment, IServer server)
        : base(hostEnvironment, server)
    {
    }

    public string MapCustomPath(string fileName, CustomFolders folder)
    {
        string folderName = folder switch
        {
            CustomFolders.Documents => "documents",
            CustomFolders.Videos => "videos",
            CustomFolders.Archives => "archives",
            _ => throw new ArgumentException("Unknown folder type")
        };

        string rootPath = hostEnvironment.WebRootPath;
        return Path.Combine(rootPath, folderName, fileName);
    }
}

Path Validation

Add validation to ensure safe file operations:
public class SafePathProvider
{
    private readonly HelperPathProvider _helper;

    public SafePathProvider(HelperPathProvider helper)
    {
        _helper = helper;
    }

    public string GetValidatedPath(string fileName, Folders folder)
    {
        // Sanitize filename
        fileName = Path.GetFileName(fileName); // Remove any path components
        
        // Check for invalid characters
        if (fileName.IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
            throw new ArgumentException("Filename contains invalid characters");

        // Prevent path traversal
        if (fileName.Contains(".."))
            throw new ArgumentException("Path traversal detected");

        return _helper.MapPath(fileName, folder);
    }
}

Best Practices

Sanitize filenames: Always validate and sanitize user-provided filenames before using them with MapPath or MapUrlPath.
Create folders before use: Ensure destination folders exist before attempting to save files. Use Directory.CreateDirectory() if needed.

File Name Sanitization

public string SanitizeFileName(string fileName)
{
    // Remove path information
    fileName = Path.GetFileName(fileName);
    
    // Remove invalid characters
    foreach (char c in Path.GetInvalidFileNameChars())
    {
        fileName = fileName.Replace(c, '_');
    }
    
    // Limit length
    if (fileName.Length > 200)
        fileName = fileName.Substring(0, 200);
    
    return fileName;
}

Ensure Folder Exists

public async Task<IActionResult> SaveFile(IFormFile file, Folders folder)
{
    string fileName = SanitizeFileName(file.FileName);
    string filePath = _helper.MapPath(fileName, folder);
    
    // Ensure directory exists
    string directory = Path.GetDirectoryName(filePath);
    if (!Directory.Exists(directory))
    {
        Directory.CreateDirectory(directory);
    }
    
    using (var stream = new FileStream(filePath, FileMode.Create))
    {
        await file.CopyToAsync(stream);
    }
    
    return Ok();
}

Generate Unique Filenames

public string GenerateUniqueFileName(string originalFileName)
{
    string extension = Path.GetExtension(originalFileName);
    string nameWithoutExtension = Path.GetFileNameWithoutExtension(originalFileName);
    string uniqueName = $"{nameWithoutExtension}_{Guid.NewGuid()}{extension}";
    return uniqueName;
}

// Usage
string uniqueFileName = GenerateUniqueFileName(file.FileName);
string path = _helper.MapPath(uniqueFileName, Folders.Images);

Testing

Unit Test Example

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Moq;
using Xunit;

public class HelperPathProviderTests
{
    [Fact]
    public void MapPath_ReturnsCorrectPath()
    {
        // Arrange
        var mockEnv = new Mock<IWebHostEnvironment>();
        mockEnv.Setup(e => e.WebRootPath).Returns("/var/www/app/wwwroot");
        
        var mockServer = new Mock<IServer>();
        var features = new FeatureCollection();
        var addressFeature = new ServerAddressesFeature();
        addressFeature.Addresses.Add("https://localhost:5001");
        features.Set<IServerAddressesFeature>(addressFeature);
        mockServer.Setup(s => s.Features).Returns(features);
        
        var helper = new HelperPathProvider(mockEnv.Object, mockServer.Object);
        
        // Act
        string path = helper.MapPath("test.jpg", Folders.Images);
        
        // Assert
        Assert.Equal("/var/www/app/wwwroot/images/test.jpg", path);
    }
    
    [Fact]
    public void MapUrlPath_ReturnsCorrectUrl()
    {
        // Similar setup as above
        // ...
        
        // Act
        string url = helper.MapUrlPath("test.jpg", Folders.Images);
        
        // Assert
        Assert.Equal("https://localhost:5001/images/test.jpg", url);
    }
}

Integration with File Upload

See the complete integration example in the File Uploads documentation.

Source Reference

  • HelperPathProvider class: Helpers/HelperPathProvider.cs:11
  • Folders enum: Helpers/HelperPathProvider.cs:6
  • MapPath method: Helpers/HelperPathProvider.cs:21
  • MapUrlPath method: Helpers/HelperPathProvider.cs:46
  • Usage in UploadFilesController: Controllers/UploadFilesController.cs:30

Build docs developers (and LLMs) love