In de hedendaagse digitale tijdperk is efficiënte beeldbehandeling cruciaal voor web-applicaties en APIs. Een van de belangrijkste aspecten van image management is compression, die helpt bij het verminderen van bestandsgrootte zonder aanzienlijk de kwaliteit te compromiseren. Deze gids leidt u door het bouwen van een dynamische beeldcompression API met behulp van Aspose.Imaging voor .NET. Aan het einde, zult u een functionele ASP.NET Core Web API die afbeeldingen accepteert en compressed output retourneert volgens query parameters (formaat, kwaliteit, resing, en meer).

Aspose.Imaging is een krachtige bibliotheek voor het werken met afbeeldingen in .NET. Het ondersteunt veel formaten en biedt robuste manipulatiefuncties, waaronder lossy (JPEG) en lossless (PNG) werkstromen.

Wat je zal bouwen

  • Het eindpunt *: POST /api/images/compress?format=jpeg&quality=75&maxWidth=1280&maxHeight=1280
  • Inputs: meerdere bestanden (afbeelding), optionele query parameters voor format/kwaliteit/resize
  • Outputs: gecomprimeerde afbeeldingstromen met correct Content-Type en caching hoofden
  • Safety: Content-type validatie, groottegrens en bewaard decode/encode

Voorwaarden

  • .NET 8 (of .Net 6+)
  • Het ASP.NET Core Web API-project
  • NuGet : Aspose.Imaging
  • Optieel: licentie initialisatie in app start-up (als u een geautoriseerde build gebruikt)

De structuur van het project (minimal)

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

Complete voorbeeld (Service + Controller)

Vervang de plaatshoudernaamruimten met de naamruimte van uw project.

/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) controleert de verliescompressie.
  • PNG-default is meestal goed voor een eerste versie; als u extra kleine png’s nodig hebt, kunt u later geavanceerde tuning toevoegen.
  • TryStripMetadata is een best-effort benadering; metadata APIs variëren per bronformaat.

/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 (DI registratie + optionele licentie)

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();

Step-by-step gids

Stap 1: Set up het project

Creëer een ASP.NET Core Web API project. Aspose.Imaging NuGet pakket. creëren de Models, Services, en Controllers mappen zoals hierboven weergegeven.

Stap 2: Configure Aspose.Imaging (optional licensing)

Als u een licentie hebt, initiër het bij start-up (zie Program.csDit vermijdt de beoordeling van watermarkten en zorgt voor volledige functionaliteit.

Stap 3: Implementeren van de compressie dienst

De ImageCompressionService:

  • Foto’s opladen via Image.Load(Stream)
  • Optioneel strijdt metadata
  • Optioneel resiseren met aspect ratio bewaard
  • Sparen op JPEG of PNG met opties die geschikt zijn voor het format

Stap 4: Maak de API-controller

ImageController Exposities POST /api/images/compress Neem een bestand en query parameters:

  • format: jpeg of png (default jpeg)
  • quality1 – 100 (alleen JPEG; standaard 80)
  • maxWidth/maxHeightBeperkingen voor downscaling
  • stripMetadataDe default true Voor een kleinere productie

Stap 5: Test de API

Gebruik een HTTP-client om een multipart/form-data verzoek met een enkel bestandveld genaamd file, plus optionele query parameters. controleer:

  • Response Content-Type wedstrijden formaat
  • De teruggekeerde bestandgrootte wordt verminderd
  • Vernieuwde werkzaamheden zoals verwacht

Design keuzes en beste praktijken

  • Format-waarde instellingen: JPEG gebruikt Quality; PNG blijft verliesloos voor voorspelbare productie.
  • Downscale voor codering: Resizing vermindert eerst pixels (grote grootte wint), vervolgens coderen snijden byten verder.
  • Sanitize inputs: bewaking van de inhoudstype, bestandgrootte, vraaggrens.
  • Streaming: Vermijd het hele bestand herhaaldelijk in het geheugen te lezen; houd de stromen kortleefd en zoektochtbaar.
  • Caching: Markert antwoorden als ongewijzigd als u naam/inhoud afkomstig maakt van deterministische inputs; anders tonen de cache-hooters naar uw gebruikssituatie.
  • Security: Valideer de inhoudstype en weigert verdachte betaalde lasten.
  • Observabiliteit: Loggrootte voor/na en gebruikte parameters; dit helpt u om standaarden te tonen.
  • Throttling: als openbaar blootgesteld, tarief-limit of vereist auth om misbruik te voorkomen.

Algemene uitbreidingen (drop-in later)

  • WebP/AVIF coderen voor nog kleinere afbeeldingen (nieuwe opties toevoegen/contentType/bestand uitbreiding in BuildOptions).
  • PNG tuning (filtering/compressie niveau) als u extra kleine verliesloze activa nodig heeft.
  • Profielen voor te stellen zoals thumbnail, preview, hires Met behulp van de bekende parameters.
  • ETags of content hashing om identieke antwoorden uit de cache te dienen.
  • Async batch eindpunt om meerdere bestanden tegelijk te compressen.

Troubleshooting

  • Hoge inputs: Verhoging RequestSizeLimit of stroom naar temp opslag.
  • Kleurige kleuren: Verzekerde kleurruimte wordt beheerd door standaard; geavanceerde gevallen kunnen uitdrukkelijke kleurtype nodig hebben.
  • ** Geen grootteverlaging (PNG)**: PNG is verliesloos; het maakt het mogelijk om terug te schakelen of naar JPEG te wisselen voor sterkere bytebesparingen.

Samenvatting

U hebt nu een productie-bereid dynamische beeldcompressie API met behulp van Aspose.Imaging. de beheerder beheert uploads en parameters; de service toepast veilig, format-bewaking compression en optionele resing, dan stroomt terug een goed getypeerde reactie met cache heads. Van hieruit kunt u meer formaten, pre-sets en caching strategieën toe te voegen om uw web stack aan te passen.

More in this category