W dzisiejszej epoce cyfrowej efektywna obsługa obrazu jest kluczowa dla aplikacji internetowych i API. Jednym z głównych aspektów zarządzania obrazem jest kompresja, która pomaga w zmniejszaniu rozmiarów plików bez znaczącego zakłócania jakości. Ten przewodnik prowadzi Cię poprzez budowę dynamicznej API kompresii obrazów za pomocą Aspose.Imaging dla .NET. Do końca, będziesz miał funkcjonalny ASP.NET Core Web API, który akceptuje obrazy i zwraca komprimowane wyniki zgodnie z parametrami zapytania (format, jakość, resing i wiele innych).

Aspose.Imaging to potężna biblioteka do pracy z obrazami w .NET. obsługuje wiele formatów i zapewnia solidne funkcje manipulacji, w tym straty (JPEG) i bez strat (PNG) przepływów roboczych.

Co będziecie budować

  • • punkt końcowy: POST /api/images/compress?format=jpeg&quality=75&maxWidth=1280&maxHeight=1280
  • Wpisy: plik wielostronny (zdjęcie), opcjonalne parametry zapytania dla formatu / jakości / rozmiaru
  • Outputs: strumień kompresyjny obrazu z poprawnym Content-Type i caching headers
  • Bezpieczeństwo: weryfikacja typu zawartości, limity rozmiaru oraz zapisywany kod / kod

Warunki

  • Źródło: .NET 6+
  • Projekt ASP.NET Core Web API
  • W Nowym Jorku: Aspose.Imaging
  • Opcjonalne: inicjalizacja licencji w aplikacji startup (jeśli używasz licencjonowanego budynku)

Struktura projektu (minimalna)

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

Pełny przykład (Service + Controller)

Zastąpić przestrzeń nazwiskową właściciela lokalu nazwą przestrzeni projektowej.

/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) kontroluje kompresję stratową.
  • Defaulty PNG są zwykle dobre dla pierwszej wersji; jeśli potrzebujesz dodatkowych małych pNG, możesz dodać zaawansowane tuning później.
  • TryStripMetadata jest najlepszym podejściem; metadata APIs różnią się w zależności od formatu źródłowego.

/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 (Zarejestruj się + licencja opcjonalna)

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

Przewodnik krok po kroku

Krok 1: Zorganizuj projekt

Tworzenie projektu ASP.NET Core Web API. Aspose.Imaging Pakiet NuGet. utworzyć Models, Services, i Controllers folderów, jak pokazano powyżej.

Krok 2: Konfiguruj Aspose.Imaging (opcjonalna licencja)

Jeśli masz licencję, inicjalizuj ją na starcie (patrz Program.csTo unika oceny znaków wodnych i zapewnia pełną funkcjonalność.

Krok 3: Wdrożenie usługi kompresji

W tym ImageCompressionService:

  • Pobierz obrazy poprzez Image.Load(Stream)
  • Opcjonalnie wyciąga metadane
  • Opcjonalnie rezygnuje z zachowanym stosunkiem aspektowym
  • Oszczędności do JPEG lub PNG z opcjami odpowiadającymi formatowi

Krok 4: Stwórz kontroler API

ImageController Ekspozycje POST /api/images/compress Przyjmowanie pliku i zapytania parametrów:

  • format: jpeg lub png (podstawowe jpeg)
  • quality1–100 (tylko JPEG; standard 80)
  • maxWidth/maxHeight• ograniczenia do spadku
  • stripMetadata• Default true Dla mniejszej produkcji

Krok 5: Przegląd API

Użyj dowolnego klienta HTTP, aby wysłać multipart/form-data żądanie z jednym polem pliku o nazwie file, plus opcjonalne parametry zapytania. sprawdź:

  • Response Content-Type Formaty meczów
  • Rozmiar pliku zwróconego jest zmniejszony
  • Rekonstrukcja prac zgodnie z oczekiwaniami

Opcje projektowe i najlepsze praktyki

  • Ustawienia format-aware: używa JPEG QualityPNG pozostaje bez strat na przewidywalny wydajność.
  • Downscale przed kodowaniem: Resizing zmniejsza piksele najpierw (największe rozmiary wygrywają), a następnie kodowanie skrótów byty dalej.
  • Sanityzuj wpisy: typ treści, rozmiar pliku, granice zapytania.
  • ** Streaming**: Unikaj czytania całego pliku w pamięci wielokrotnie; utrzymanie strumieni krótkoterminowych i wyszukiwalnych.
  • Caching: Zaznacz odpowiedź jako niezmienna, jeśli wyodrębnisz nazwę / zawartość z wpisów deterministycznych; w przeciwnym razie naciśnij nagłówki cache do swojego przypadku użycia.
  • Bezpieczeństwo: weryfikuj typ treści i odrzuć podejrzane obciążenia.
  • Obserwowalność: Rozmiary rejestru przed/po i używane parametry; to pomaga ustalić wady.
  • Przestrzeganie: Jeśli wystawione publicznie, limit stopy lub wymaga auth w celu zapobiegania nadużyciom.

Wspólne rozszerzenia (drop-in później)

  • Kodery WebP/AVIF dla jeszcze mniejszych obrazów (dodaj nowe opcje/contentTypeRozszerzenie pliku w BuildOptions).
  • PNG tuning (poziom filtracji/kompresji) jeśli potrzebujesz dodatkowo małych aktywów bez strat.
  • Predstawianie profilów jak thumbnail, preview, hires Wykorzystywanie znanych parametrów.
  • Tags lub hashing zawartości, aby służyć identycznym odpowiedziom z cache.
  • Async batch końcowy punkt do kompresji wielu plików naraz.

Troubleshooting

    • Wielkie wkłady *: Wzrost RequestSizeLimit lub przepływ do temp przechowywania.
  • Czerwone kolory: Zabezpieczony przestrzeń kolorów jest obsługiwany przez domyślne; zaawansowane przypadki mogą wymagać wyraźnego typu koloru.
  • ** Brak redukcji wielkości (PNG)**: PNG jest bez strat; umożliwia ponowne rozmieszczenie lub przejście do JPEG dla silniejszych oszczędności bytu.

Podsumowanie

Teraz masz gotowy do produkcji dynamiczny kompresji obrazu API za pomocą Aspose.Imaging. sterownik obsługuje przesyłki i parametry; usługa stosuje bezpieczne, format-aware kompresię i opcjonalne odtwarzanie, a następnie przenosi odpowiednio typowane odpowiedzi z nagłówkami cache. Odtąd można dodać więcej formatów, presetów i strategii caching, aby dopasować się do twojego web stack.

More in this category