using Microsoft.AspNetCore.Http; namespace PowderCoating.Application.Services; /// /// Shared file validation and content-type resolution used across all blob storage services. /// public static class BlobFileHelper { /// /// Validates an uploaded file against an extension allowlist and a maximum size. /// Returns the normalized (lowercase) extension on success so callers do not re-derive it. /// public static (bool IsValid, string Extension, string Error) ValidateUpload( IFormFile? file, string[] allowedExtensions, long maxBytes) { if (file == null || file.Length == 0) return (false, string.Empty, "No file provided."); if (file.Length > maxBytes) return (false, string.Empty, $"File exceeds the {maxBytes / 1024 / 1024} MB limit."); var extension = Path.GetExtension(file.FileName).ToLowerInvariant(); if (string.IsNullOrEmpty(extension) || !allowedExtensions.Contains(extension)) return (false, string.Empty, $"File type not allowed. Allowed: {string.Join(", ", allowedExtensions)}."); return (true, extension, string.Empty); } /// /// Maps a file extension to its MIME content type, covering common image formats and /// document types. Falls back to application/octet-stream. /// public static string GetContentType(string extension) => extension switch { ".jpg" or ".jpeg" => "image/jpeg", ".png" => "image/png", ".gif" => "image/gif", ".webp" => "image/webp", ".svg" => "image/svg+xml", ".pdf" => "application/pdf", ".doc" => "application/msword", ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document", ".txt" => "text/plain", _ => "application/octet-stream" }; /// /// Strips OS-invalid filename characters from a base filename (no extension), replacing /// them with underscores to produce a safe blob path segment. /// public static string SanitizeFileName(string fileName) { var sanitized = string.Join("_", fileName.Split(Path.GetInvalidFileNameChars())); return string.IsNullOrWhiteSpace(sanitized) ? "file" : sanitized; } }