V dnešním digitálním věku je efektivní zpracování obrazu rozhodující pro webové aplikace a API. Jedním z klíčových aspektů image managementu je komprese, která pomáhá snižovat velikost souborů bez výrazného narušení kvality. Tento průvodce vás vede prostřednictvím budování dynamické image compression API pomocí Aspose.Imaging pro .NET. Konečně budete mít funkční ASP.NET Core Web API, který přijímá obrázky a vrací komprimovaný výsledek podle vyhledávacích parametrů (formát, kvalita, resing a další).

Aspose.Imaging je mocná knihovna pro práci s obrázky v .NET. Podporuje mnoho formátů a poskytuje robustní funkce manipulace, včetně ztráty (JPEG) a bezztrát (PNG) pracovních toků.

Co budeš budovat

  • Závěrečný bod: POST /api/images/compress?format=jpeg&quality=75&maxWidth=1280&maxHeight=1280
  • Vstupy: Multipartový soubor (obrázek), volitelné parametry dotazu pro formát / kvalitu / rozměr
  • Outputs: Komprimovaný tok obrazu s správným Content-Type a caching hlavice
  • Bezpečnost: Validace typu obsahu, omezení velikosti a zachovaný dekód/ekód

Předpoklady

  • • NET 8 (nebo .NET 6+)
  • Projekt ASP.NET Core Web API
  • V tomto případě: Aspose.Imaging
  • Volitelné: iniciování licence v aplikaci Startup (pokud používáte licenční stavbu)

Struktura projektu (minimální)

/Controllers
  ImageController.cs
/Services
  ImageCompressionService.cs
/Models
  CompressionRequest.cs
Program.cs
appsettings.json

Kompletní příklad (Service + Controller)

Nahraďte název místa hostitele jménem s názvem prostoru vašeho projektu.

/Models/CompressionRequest.cs

namespace ImageApi.Models;

public sealed class CompressionRequest
{
    // "jpeg" or "png"
    public string Format { get; init; } = "jpeg";

    // 1..100 (applies to JPEG only; PNG is lossless)
    public int? Quality { get; init; } = 80;

    // Optional resize bounds; image is resized preserving aspect ratio if either is provided.
    public int? MaxWidth { get; init; }
    public int? MaxHeight { get; init; }

    // If true, strip metadata (EXIF, IPTC) where applicable to reduce size further.
    public bool StripMetadata { get; init; } = true;

    // Guardrails
    public void Validate()
    {
        var fmt = Format?.ToLowerInvariant();
        if (fmt is not "jpeg" and not "png")
            throw new ArgumentException("Unsupported format. Use 'jpeg' or 'png'.");

        if (Quality is { } q && (q < 1 || q > 100))
            throw new ArgumentException("Quality must be between 1 and 100.");

        if (MaxWidth is { } w && w <= 0) throw new ArgumentException("MaxWidth must be positive.");
        if (MaxHeight is { } h && h <= 0) throw new ArgumentException("MaxHeight must be positive.");
    }
}

/Services/ImageCompressionService.cs

using Aspose.Imaging;
using Aspose.Imaging.ImageOptions;
using ImageApi.Models;

namespace ImageApi.Services;

public interface IImageCompressionService
{
    Task<(MemoryStream output, string contentType, string fileExt)> CompressAsync(
        Stream input, CompressionRequest req, CancellationToken ct = default);
}

public sealed class ImageCompressionService : IImageCompressionService
{
    private readonly ILogger<ImageCompressionService> _logger;

    public ImageCompressionService(ILogger<ImageCompressionService> logger)
    {
        _logger = logger;
    }

    public async Task<(MemoryStream output, string contentType, string fileExt)> CompressAsync(
        Stream input, CompressionRequest req, CancellationToken ct = default)
    {
        req.Validate();

        // Defensive copy to a seekable stream
        var inbound = new MemoryStream();
        await input.CopyToAsync(inbound, ct).ConfigureAwait(false);
        inbound.Position = 0;

        // Load image via Aspose.Imaging
        using var image = Image.Load(inbound);

        // Optional: strip metadata (where applicable)
        if (req.StripMetadata)
        {
            TryStripMetadata(image);
        }

        // Optional resize (preserve aspect ratio)
        if (req.MaxWidth.HasValue || req.MaxHeight.HasValue)
        {
            ResizeInPlace(image, req.MaxWidth, req.MaxHeight);
        }

        // Choose encoder and options
        string fmt = req.Format.ToLowerInvariant();
        var (options, contentType, ext) = BuildOptions(fmt, req.Quality);

        // Save to output
        var output = new MemoryStream();
        image.Save(output, options);
        output.Position = 0;

        _logger.LogInformation("Compressed image to {Bytes} bytes as {Ext}", output.Length, ext);
        return (output, contentType, ext);
    }

    private static void ResizeInPlace(Image image, int? maxW, int? maxH)
    {
        var w = image.Width;
        var h = image.Height;

        double scaleW = maxW.HasValue ? (double)maxW.Value / w : 1.0;
        double scaleH = maxH.HasValue ? (double)maxH.Value / h : 1.0;
        double scale = Math.Min(scaleW, scaleH);

        if (scale < 1.0)
        {
            int newW = Math.Max(1, (int)Math.Round(w * scale));
            int newH = Math.Max(1, (int)Math.Round(h * scale));
            image.Resize(newW, newH);
        }
    }

    private static (ImageOptionsBase options, string contentType, string ext) BuildOptions(string fmt, int? quality)
    {
        switch (fmt)
        {
            case "jpeg":
            {
                var q = quality ?? 80;
                var jpeg = new JpegOptions { Quality = q };
                return (jpeg, "image/jpeg", "jpg");
            }
            case "png":
            {
                // PNG is lossless; using defaults ensures broad compatibility.
                // Many PNG tunables exist, but defaults are safe and effective.
                var png = new PngOptions();
                return (png, "image/png", "png");
            }
            default:
                throw new ArgumentOutOfRangeException(nameof(fmt), "Unsupported format.");
        }
    }

    private static void TryStripMetadata(Image image)
    {
        try
        {
            // Not every format exposes EXIF/IPTC the same way; a best-effort clear:
            if (image is RasterImage raster)
            {
                raster.RemoveAllFonts();
                raster.SetPropertyItems(Array.Empty<PropertyItem>());
            }
        }
        catch
        {
            // Non-fatal; ignore if format doesn't support these operations
        }
    }
}

Notes

  • JpegOptions.Quality (1–100) kontroluje ztrátu komprese.
  • Předpoklady PNG jsou obvykle dobré pro první verzi; pokud potřebujete extrémně malé P NG, můžete později přidat pokročilé tuning.
  • TryStripMetadata Je to nejlepší přístup; metadata APIs se liší podle zdrojového formátu.

/Controllers/ImageController.cs

using ImageApi.Models;
using ImageApi.Services;
using Microsoft.AspNetCore.Mvc;

namespace ImageApi.Controllers;

[ApiController]
[Route("api/images")]
public sealed class ImageController : ControllerBase
{
    private static readonly HashSet<string> AllowedContentTypes = new(StringComparer.OrdinalIgnoreCase)
    {
        "image/jpeg", "image/png", "image/gif", "image/webp", "image/bmp", "image/tiff"
    };

    private readonly IImageCompressionService _svc;
    private readonly ILogger<ImageController> _logger;

    public ImageController(IImageCompressionService svc, ILogger<ImageController> logger)
    {
        _svc = svc;
        _logger = logger;
    }

    // POST /api/images/compress?format=jpeg&quality=75&maxWidth=1280&maxHeight=1280
    [HttpPost("compress")]
    [RequestSizeLimit(25_000_000)] // 25 MB cap; adjust to your needs
    public async Task<IActionResult> Compress(
        [FromQuery] string? format,
        [FromQuery] int? quality,
        [FromQuery] int? maxWidth,
        [FromQuery] int? maxHeight,
        [FromQuery] bool stripMetadata = true,
        IFormFile? file = null,
        CancellationToken ct = default)
    {
        if (file is null || file.Length == 0)
            return BadRequest("No file uploaded.");

        if (!AllowedContentTypes.Contains(file.ContentType))
            return BadRequest("Unsupported content type. Upload a common raster image (JPEG, PNG, GIF, WebP, BMP, TIFF).");

        var req = new CompressionRequest
        {
            Format = string.IsNullOrWhiteSpace(format) ? "jpeg" : format!,
            Quality = quality,
            MaxWidth = maxWidth,
            MaxHeight = maxHeight,
            StripMetadata = stripMetadata
        };

        await using var input = file.OpenReadStream();
        var (output, contentType, ext) = await _svc.CompressAsync(input, req, ct);

        // Strong caching for immutable responses (tune for your app/CDN)
        Response.Headers.CacheControl = "public,max-age=31536000,immutable";

        return File(output, contentType, fileDownloadName: BuildDownloadName(file.FileName, ext));
    }

    private static string BuildDownloadName(string originalName, string newExt)
    {
        var baseName = Path.GetFileNameWithoutExtension(originalName);
        return $"{baseName}-compressed.{newExt}";
    }
}

Program.cs (přihlašování + volitelná licence)

using Aspose.Imaging;
using ImageApi.Services;

var builder = WebApplication.CreateBuilder(args);

// Optional: initialize Aspose license from a file or stream if you have one
// var license = new Aspose.Imaging.License();
// license.SetLicense("Aspose.Total.lic");

builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddSingleton<IImageCompressionService, ImageCompressionService>();

var app = builder.Build();

app.UseRouting();
app.UseAuthorization();
app.MapControllers();

// Enable for local testing
app.UseSwagger();
app.UseSwaggerUI();

app.Run();

Krok za krokem průvodce

Krok 1: Nastavení projektu

Vytvořte ASP.NET Core Web API projekt. Aspose.Imaging Vybavte si balíček - vytvořte Models, Services, a Controllers mapy, jak je uvedeno výše.

Krok 2: Nastavení Aspose.Imaging (volitelná licence)

Pokud máte licenci, iniciujte ji na startupu (viz Program.csTím se vyhýbá hodnocení vodních ukazatelů a zajišťuje plnou funkčnost.

Krok 3: Provádění kompresní služby

a to ImageCompressionService:

  • Nahrávat obrázky přes Image.Load(Stream)
  • Opcionálně střípí metadata
  • Opcionálně resizuje s aspektním poměrem zachovaným
  • Ušetřete na JPEG nebo PNG s možností, které odpovídají formátu

Krok 4: Vytvořte ovládací prvek API

ImageController Expozice POST /api/images/compress Využijte soubor a dotazní parametry:

  • format: jpeg nebo png (defaultní jpeg)
  • quality1 – 100 (pouze JPEG; výchozí 80)
  • maxWidth/maxHeightZávěry pro downscaling
  • stripMetadata• default true Pro menší produkci

Krok 5: Vyzkoušejte API

Využijte jakéhokoli klienta HTTP k odesílání multipart/form-data požadavek s jediným souborovým pole jmenované file, plus volitelné vyhledávací parametry. zkontrolujte:

  • Response Content-Type Formát zápasů
  • Velikost vráceného souboru je snížena
  • Rekonstrukce práce podle očekávání

Designové volby a nejlepší postupy

  • ** Formátové nastavení**: JPEG používá QualityPNG zůstává bez ztráty pro předvídatelnou produkci.
  • Downscale před kódováním: Resizing snižuje pixely nejprve (větší velikost zvítězí), pak kódování shrinks byty dále.
  • Sanitizujte vstupy: typ obsahu, velikost souboru, limity dotazu.
  • Streaming: Vyhýbejte se čtení celého souboru do paměti opakovaně; udržujte toky krátkodobé a vyhledatelné.
  • Caching: Značka odpovídá jako nezměnitelná, pokud získáte jméno/související obsah z deterministických vstupů; jinak do vašeho případu použijete název cache.
  • Bezpečnost: Validujte typ obsahu a odmítněte podezřelé platby.
  • Observovatelnost: Přihlašovací rozměry před/po a použité parametry; to vám pomáhá vymazat předvolby.
  • Trotling: Pokud je veřejně vystavena, sazba-limit nebo požaduje auth k předcházení zneužívání.

Společné rozšíření (drop-in později)

  • WebP/AVIF kódování pro ještě menší obrázky (přidat nové možnosti/contentTypeRozšíření souboru v BuildOptions).a).
  • PNG tuning (filtrovací / kompresní úroveň) pokud potřebujete extra malé neztrácené aktivy.
  • Představení profilů jako thumbnail, preview, hires Zobrazit známé parametry.
  • ETags nebo obsahový hashing sloužící pro stejné odpovědi z cache.
  • Async batch endpoint pro kompresi několika souborů najednou.

Troubleshooting

  • Velké vstupy: Zvýšení RequestSizeLimit nebo stream do temp skladování.
  • Bojové barvy: Zabezpečení barevného prostoru se řídí výchozími podmínkami; pokročilé případy mohou vyžadovat výslovný typ barv.
  • Žádné snížení velikosti (PNG): PNG je bez ztrát; umožňuje resize nebo přechod na JPEG pro silnější úspory bytu.

Shrnutí

Nyní máte výrobní připravenou dynamickou kompresní API s použitím Aspose.Imaging. Ovladač zvládá nahrávání a parametry; služba aplikuje bezpečné, formátově informované kompresi a volitelné resing, pak zpět proudí správně vytisknutou odpověď s cache titulky. Zde můžete přidat další formáty, předvolby a caching strategie, aby se vyhovovaly vaší webové stack.

More in this category