I dagens digitala tidsålder är effektiv bildhantering avgörande för webbapplikationer och APIs. En av de viktigaste aspekterna av image management är kompression, som hjälper till att minska filstorlekar utan att signifikant kompromissa med kvaliteten. Denna guide går dig genom att bygga en dynamisk bildkompression API med Aspose.Imaging för .NET. Till slut kommer du att ha en funktionell ASP.NET Core Web API som accepterar bilder och returnerar komprimerad output enligt sökparametrar (format, kvalitet, återställning, och mer).

Aspose.Imaging är ett kraftfullt bibliotek för att arbeta med bilder i .NET. Det stöder många format och ger robusta manipulationsfunktioner, inklusive förluster (JPEG) och förlustfria (PNG) arbetsflöden.

Vad du ska bygga

  • ”Endpoint” är: POST /api/images/compress?format=jpeg&quality=75&maxWidth=1280&maxHeight=1280
  • Inputs: Multipart fil (bilden), valfrågningsparametrar för format/kvalitet/storlek
  • Utgångar: Komprimerad bildström med rätt Content-Type och caching headers
  • Säkerhet: Validering av innehållstyp, storleksgränser och bevarad decod/encod

förutsättningar

  • .NET 8 (eller .Net 6+)
  • ASP.NET Core Web API projekt
  • Nu är: Aspose.Imaging
  • Alternativt: licens initialisering i appstart (om du använder en licensierad byggnad)

Projektstruktur (minimalt)

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

Fullständigt exempel (Service + Controller)

Ersätt platsinnehavarens namnutrymmen med ditt projektnamnutrymme.

/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) kontrollerar förlustkompression.
  • PNG-default är vanligtvis bra för en första version; om du behöver extra små png, kan du lägga till avancerad tuning senare.
  • TryStripMetadata är en best-effort tillvägagångssätt; metadata APIs varierar beroende på källformat.

/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 registrering + valfri licens)

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

Steg för steg guide

Steg 1: Ställ in projektet

Skapa ett ASP.NET Core Web API-projekt. Aspose.Imaging NuGet paket. skapa den Models, Services, och Controllers mappar som visas ovan.

Steg 2: Konfigurera Aspose.Imaging (alternativ licens)

Om du har en licens, initiera den vid start (se Program.csDetta undviker utvärdering av vattenmärken och säkerställer full funktionalitet.

Steg 3: Implementera kompressionstjänsten

och den ImageCompressionService:

  • Ladda upp bilder via Image.Load(Stream)
  • Optionellt stripa metadata
  • Optionellt residerar med aspektförhållande bevarad
  • Spara till JPEG eller PNG med format lämpliga alternativ

Steg 4: Bygg API-kontrollen

ImageController Exponeringar POST /api/images/compress Ta en fil och fråga parametrar:

  • format: jpeg eller png (default för jpeg)
  • quality1–100 (endast JPEG; standard 80)
  • maxWidth/maxHeight• Gränser för nedskärning
  • stripMetadataFörfalskning true För mindre produktion

Steg 5: Testa API

Använd någon HTTP-klient för att skicka en multipart/form-data begäran med ett enda filfält som heter file, plus valfria frågeparametrar. kontrollera:

  • Response Content-Type Matcher format
  • Återlämnade filstorlek minskas
  • Återställa arbetet som förväntat

Designval och bästa praxis

  • Formatvaruinställningar: JPEG använder QualityPNG förblir förlustlös för förutsägbart produktion.
  • Downscale före kodning: Återkoppling minskar pixlar först (största storleken vinner), sedan kodningen skruvar byter vidare.
  • Sanitize inputs: Guard innehållstyp, filstorlek, begränsningar för frågor.
  • Streaming: Undvik att läsa hela filen i minnet upprepade gånger; hålla strömmar kortlivade och sökbara.
  • Caching: Markera svar som oförändrade om du härrör namn/innehåll från deterministiska inmatningar; annars tunna cache headers till ditt användningsfall.
  • Säkerhet: Validerar innehållstyp och avvisar misstänkta laddningar.
  • Observabilitet: Log storlekar före/efter och parametrar som används; detta hjälper dig att tunna standard.
  • Trotling: Om uttryckt offentligt, räntegräns eller kräver auth för att förhindra missbruk.

Vanliga förlängningar (drop-in senare)

  • WebP/AVIF kodare för ännu mindre bilder (lägga till nya alternativ/contentType/fil förlängning i BuildOptions).
  • PNG tuning (filtering/komprimeringsnivå) om du behöver extra små förlustfria tillgångar.
  • Förställa profiler som thumbnail, preview, hires kartläggning till kända parametrar.
  • ETags eller innehållshashing för att tjäna identiska svar från cache.
  • Async batch slutpunkten för att komprimera flera filer på en gång.

Troubleshooting

  • Hög inmatning: Öka RequestSizeLimit eller ström till temp lagring.
  • Röda färger: Säker färgutrymme hanteras enligt standard; avancerade fall kan kräva uttrycklig färgtyp.
  • Ingen storlek minskning (PNG): PNG är förlustlös; gör det möjligt att återställa eller byta till JPEG för starkare bytebesparingar.

Sammanfattning

Du har nu en produktionsbered dynamisk bildkompression API med hjälp av Aspose.Imaging. Controller hanterar laddningar och parametrar; tjänsten tillämpar säker, format-bevakning kompression och valfri resing, sedan strömmar tillbaka en korrekt typad respons med cache headers. Från här kan du lägga till fler format, förinställningar, och caching strategier för att passa din webbstack.

More in this category