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.cs
Cela é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
oupng
(par défautjpeg
)quality
: 1 à 100 (uniquement JPEG; défaut 80)maxWidth
/maxHeight
Les limites pour la baissestripMetadata
• Défauttrue
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
Quality
Le 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/
contentType
Extension des fichiers enBuildOptions
). - 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.
- Préparer les profils * comme
- É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
- Optimiser les GIF animés dans .NET en utilisant Aspose.Imaging
- Optimiser les TIFF multi-page pour l'archivage dans .NET avec Aspose
- Animations à base de données dans .NET avec Aspose.Imaging
- Comparer Lossy vs. Lossless Image Compression dans .NET en utilisant Aspose.Imaging
- Compression d'image sans perte et déterminée de qualité dans .NET avec Aspose.Imaging