I dagens digitale tidsalder er effektiv billedbehandling afgørende for webapplikationer og APIs. En af de vigtigste aspekter af billedsstyring er kompression, som hjælper med at reducere filstørrelser uden signifikant kompromittering af kvaliteten. Denne guide går dig gennem opbygning af en dynamisk image-kompression API ved hjælp af Aspose.Imaging for .NET. Til sidst vil du have en funktionel ASP.NET Core Web API, der accepterer billeder og returnerer komprimeret udgang efter efterspørgsel parametre (format, kvalitet, resing, og mere).

Aspose.Imaging er en kraftfuld bibliotek til at arbejde med billeder i .NET. Det understøtter mange formater og giver robuste manipulationsfunktioner, herunder tab (JPEG) og tabsløse (PNG) arbejdsprocesser.

Hvad du vil bygge

  • ”Endpoint” er: POST /api/images/compress?format=jpeg&quality=75&maxWidth=1280&maxHeight=1280
  • Inputs: Multipart fil (billede), valgfrie spørgeskemaer for format/kvalitet/resize
  • Outputs: Komprimeret billedstrøm med korrekt Content-Type og caching headers
  • Sikkerhed: Validering af indholdstype, størrelsesgrænser og gemt dekode/encode

Forudsætninger

  • • NET 8 (eller .NET 6+)
  • ASP.NET Core Web API projekt
  • Det er nu: Aspose.Imaging
  • Optionelt: licens initialisering i app startups (hvis du bruger en licensieret bygning)

Projektstrukturen (minimalt)

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

Et komplet eksempel (Service + Controller)

Udskift pladserens navnepladser med dit projekt.

/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) kontrollerer tab kompression.
  • PNG-defekter er typisk fine for en første version; hvis du har brug for ekstra små pNG’er, kan du tilføje avanceret tuning senere.
  • TryStripMetadata er en best-effort tilgang; metadata APIs varierer afhængigt af kildeformat.

/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 + valgfri 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();

Step-by-Step Guide

Første skridt: Sæt projektet op

Skab et ASP.NET Core Web API projekt. Aspose.Imaging NuGet pakke. skab den Models, Services, og Controllers arkiver som vist ovenfor.

Trin 2: Konfigurer Aspose.Imaging (valglig licens)

Hvis du har en licens, skal du initialisere den ved start (se Program.csDette undgår evaluering af vandmærker og sikrer fuld funktionalitet.

Trin 3: Implementering af kompressionstjenesten

Den ImageCompressionService:

  • Oplader billeder via Image.Load(Stream)
  • Optionelt striber metadata
  • Optionelt residerer med aspektforhold bevaret
  • Sparer til JPEG eller PNG med formatmæssige muligheder

Trin 4: Opbyg API-kontroller

ImageController Udstillinger POST /api/images/compress Tag en fil og spørge parametre:

  • format: jpeg eller png (default af jpeg)
  • quality1–100 (kun JPEG; standard 80)
  • maxWidth/maxHeightGrænser for nedskillelse
  • stripMetadataaf default true For mindre udbytte

Trin 5: Testning af branden

Brug ethvert HTTP-klient til at sende en multipart/form-data Forespørgsel med et enkelt filfelt, der hedder file, plus valgfrie spørgeskemaer. Kontrollér:

  • Response Content-Type Kampe i format
  • Returnerede filer reduceres
  • Gennemførelse af arbejde som forventet

Designvalg og bedste praksis

  • Format-varselindstillinger: JPEG bruger QualityPNG forbliver tabløs for forudsigelig produktion.
  • Downscale før kodning: Resizing reducerer piksler først (største størrelser vinder), derefter koder skridt byte yderligere.
  • Sanitize inputs: Guard indholdstype, filstørrelse, forespørgselsegrænser.
  • Streaming: Undgå at læse hele filen ind i hukommelsen gentagne gange; holde strømme kortlivede og søgbare.
  • Caching: Markér svar som uændret, hvis du afgiver navn/indhold fra deterministiske indtægter; ellers tune cache-hovedere til din brug.
  • Sikkerhed: Validerer indholdstypen og afviser mistænkelige betalinger. overveje at scanne for malformede billeder.
  • Observabilitet: Log størrelser før/efter og parametre, der bruges; dette hjælper dig med at tune forekomster.
  • Throttling: Hvis udsat offentligt, rentegrænse eller kræve auth for at forhindre misbrug.

Gennemsigtige udvidelser (drop-in senere)

  • WebP/AVIF koder til endnu mindre billeder (tilføj nye muligheder/contentTypeFilforlængelse i BuildOptions).
  • PNG tuning (filtering/kompression niveau) hvis du har brug for ekstra små, tabløse aktiver.
  • ** Preset profiler** som thumbnail, preview, hires Mapping til kendte parametre.
  • ETags eller indhold hashing til at tjene identiske svar fra cache.
  • Async batch endpoint til at komprimere flere filer på én gang.

Troubleshooting

  • ** Store indtægter**: Øget RequestSizeLimit eller strøm til temp opbevaring.
  • Range farver: Sikker farvespace behandles af standard; avancerede tilfælde kan kræve en udtrykkelig farve type.
  • Ingen størrelsesreduktion (PNG): PNG er tabløst; tillader resize eller skifte til JPEG for stærkere bytebesparelser.

Samlinger

Du har nu en produktionsberettiget dynamisk image kompression API ved hjælp af Aspose.Imaging. Controller håndterer opladninger og parametre; tjenesten anvender sikker, format-bevidst komprimering og valgfri resing, så strømmer tilbage en korrekt typet respons med cache heads. Fra her kan du tilføje flere formater, forudset, og caching strategier til at matche din web stack.

More in this category