Dans l’ère numérique d’aujourd’hui, la gestion efficace des images est cruciale pour les applications Web et les APIs. L’un des aspects clés de la prise en charge de l’image est la compression, qui aide à réduire les tailles de fichiers sans compromettre considérablement la qualité. Ce guide vous guide à travers la construction d’une API dynamique de comprimation image en utilisant Aspose.Imaging pour .NET. À la fin, vous aurez une API fonctionnelle ASP.NET Core Web qui accepte les images et retourne la sortie compressée selon les paramètres de requête (format, qualité, recouvrement, et plus).

Aspose.Imaging est une bibliothèque puissante pour travailler avec des images dans .NET. Il prend en charge de nombreux formats et fournit des fonctionnalités de manipulation robustes, y compris les flux de travail perdus (JPEG) et sans pertes (PNG).

Ce que vous construirez

  • « Endpoint » : POST /api/images/compress?format=jpeg&quality=75&maxWidth=1280&maxHeight=1280
  • Inputs: fichier multipart (image), paramètres de requête facultatifs pour le format/qualité/resize
  • ** Outputs**: flux d’image compressé avec correct Content-Type et les chevaux de caching
  • Sécurité: validation du type de contenu, limites de taille et décode/encode gardé

Principaux

  • NET 8 (ou nET 6+)
  • Le projet ASP.NET Core Web API
  • Nouveau : Aspose.Imaging
  • Optionnelle: initialisation de la licence dans l’application startup (si vous utilisez un bâtiment autorisé)

La structure du projet (minimale)

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

Exemple complet (service + contrôleur)

Remplacer les espaces de nom du locataire par le nom de votre projet.

/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) contrôle la compression de perte.
  • Les défauts de PNG sont généralement bons pour une première version; si vous avez besoin de pNG plus petits, vous pouvez ajouter un tuning avancé plus tard.
  • TryStripMetadata Il s’agit d’une approche la plus efficace ; les APIs métadonnées varient selon le format source.

/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 (enregistrement + licence facultative)

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

Guide étape par étape

Étape 1 : Démarrer le projet

Créer un projet ASP.NET Core Web API. Aspose.Imaging NouGet package. Créer le Models, Services, et Controllers des dossiers comme indiqué ci-dessus.

Étape 2 : Configurer Aspose.Imaging (licencement facultatif)

Si vous avez une licence, initialisez-la à Startup (voir Program.csCela évitera l’évaluation des marques d’eau et assure la pleine fonctionnalité.

Étape 3 : mettre en œuvre le service de compression

Le ImageCompressionService:

  • Téléchargez des images via Image.Load(Stream)
  • Optionnellement couper les métadonnées
  • Résistance optionnelle avec un rapport d’aspect préservé
  • Économiser à JPEG ou PNG avec des options adaptées au format

Étape 4 : Créer le contrôleur API

ImageController Expositions POST /api/images/compress Un fichier et des paramètres de query :

  • format: jpeg ou png (par défaut jpeg)
  • quality: 1 à 100 (uniquement JPEG; défaut 80)
  • maxWidth/maxHeightLes limites pour la baisse
  • stripMetadata• Défaut true Pour une production plus petite

Étape 5 : Testez l’API

Utilisez un client HTTP pour envoyer un multipart/form-data demande avec un seul champ de fichier nommé file, plus des paramètres de query facultatifs. Vérifier :

  • Response Content-Type Formats de match
  • La taille du fichier retourné est réduite
  • Récupérer les travaux comme prévu

Choix de design et meilleures pratiques

  • ** Configuration de format-avertissement**: JPEG utilise QualityLe PNG reste sans perte pour une production prévisible.
  • Downscale avant l’encodage: la récidive réduit les pixels d’abord (la plus grande taille gagne), puis la codage accroche les bytes plus loin.
  • Sanitize entrées: Type de contenu de garde, taille de fichier, limites de requête.
  • Streaming: Évitez de lire l’ensemble du fichier dans la mémoire à plusieurs reprises; gardez les flux courts et recherchables.
  • Caching: Marquez les réponses comme immuables si vous dérivez le nom/contenu des entrées déterministes; autrement, appuyez sur les titres de cache dans votre cas d’utilisation.
  • Sécurité: Vérifiez le type de contenu et rejetez les charges soupçonnées.
  • Observabilité: Les tailles d’enregistrement avant/après et les paramètres utilisés; cela vous aide à résoudre les défauts.
  • Throttling: Si exposé publiquement, limite de taux ou exige auth pour prévenir l’abus.

Extensions communes (drop-in plus tard)

  • WebP/AVIF codes pour des images encore plus petites (ajouter de nouvelles options/contentTypeExtension des fichiers en BuildOptions).
  • PNG tuning (niveau de filtration/compression) si vous avez besoin d’actifs extra-petits sans perte.
    • Préparer les profils * comme thumbnail, preview, hires Mettre en valeur les paramètres connus.
  • Étags ou hashing de contenu pour servir les réponses identiques du cache.
  • Async batch endpoint pour compresser plusieurs fichiers à la fois.

Troubleshooting

  • Résumé de l’enregistrement : augmentation RequestSizeLimit ou à la température de stockage.
  • Les couleurs vertes: L’espace de couleur assuré est traité par défaut; les cas avancés peuvent nécessiter un type de couleurs explicite.
  • Aucune réduction de taille (PNG): PNG est sans perte; permet de redémarrer ou de passer à JPEG pour des économies de byte plus fortes.

Résumé

Vous avez maintenant une API de compression d’image dynamique prête à la production en utilisant Aspose.Imaging. Le contrôleur gère les téléchargements et les paramètres; le service s’applique en toute sécurité, la comprimation informelle et la réanimation facultative, puis revient une réponse correctement typiée avec les têtes de cache. D’ici, vous pouvez ajouter plus de formats, prédécesseurs et stratégies de cachage pour correspondre à votre stack web.

More in this category